Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

В la primera part Vaig intentar dir als enginyers electrònics aficionats que van sorgir dels pantalons Arduino com i per què haurien de llegir fulls de dades i altra documentació per als microcontroladors. El text va resultar ser gran, així que em vaig comprometre a mostrar exemples pràctics en un article separat. Bé, es deia carregador...

Avui us mostraré com utilitzar fulls de dades per resoldre tasques bastant senzilles, però necessàries per a molts projectes amb controladors STM32 (Blue Pill) i STM8. Tots els projectes de demostració estan dedicats als meus LED preferits, els il·luminarem en grans quantitats, per a això haurem d'utilitzar tot tipus de perifèrics interessants.

El text va tornar a resultar enorme, així que per comoditat estic fent el contingut:

STM32 Blue Pill: 16 LED amb controlador DM634
STM8: Configuració de sis pins PWM
STM8: 8 LED RGB en tres pins, interrupcions

Exempció de responsabilitat: no sóc enginyer, no pretenc tenir coneixements profunds en electrònica, l'article està pensat per a aficionats com jo. De fet, fa dos anys em vaig considerar com el públic objectiu. Si algú m'hagués dit aleshores que no fa por llegir fulls de dades d'un xip desconegut, no hauria passat gaire temps buscant alguns fragments de codi a Internet i inventant crosses amb tisores i guix adhesiu.

L'objectiu d'aquest article són les fitxes de dades, no els esborranys, de manera que el codi pot no estar massa polit i sovint una crossa. Els projectes en si són molt senzills, tot i que són adequats per al primer coneixement d'un xip nou.

Espero que el meu article ajudi algú en una etapa similar de submergir-se en un hobby.

STM32

16 LED amb DM634 i SPI

Petit projecte amb Blue Pill (STM32F103C8T6) i controlador LED DM634. Amb l'ajuda de fulls de dades, tractarem el controlador, els ports STM IO i configurarem SPI.

DM634

Xip taiwanès amb sortides PWM de 16 x 16 bits, es pot encadenar. El model més jove de 12 bits es coneix per un projecte domèstic paquet de llum. En un moment, escollint entre DM63x i el conegut TLC5940, em vaig optar per DM per diversos motius: 1) TLC a Aliexpress és definitivament fals, però aquest no ho és; 2) DM té un PWM autònom amb el seu propi generador de freqüència; 3) es podria comprar a bon preu a Moscou i no esperar un paquet d'Ali. I, per descomptat, va ser interessant aprendre a controlar el xip tu mateix i no utilitzar una biblioteca ja feta. Els xips ara es presenten principalment al paquet SSOP24, són fàcils de soldar a l'adaptador.

Com que el fabricant és taiwanès, fitxa de dades to the xip està escrit en xinès anglès, el que significa que serà divertit. Primer mira el pinoutConnexió de pins) per entendre quina pota connectar què i una descripció dels pins (Descripció del pin). 16 pins:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Enfonsa fonts de corrent continu (desguàs obert)

Fregadero / Sortida de drenatge obert - estoc; font de corrent entrant; una sortida connectada a terra en estat actiu: els LED estan connectats al controlador mitjançant càtodes. Elèctricament, això no és, per descomptat, un "desguàs obert" (desguàs obert), però a les fitxes de dades es troba sovint una designació d'aquest tipus per a sortides en mode drenatge.

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Resistències externes entre REXT i GND per establir el valor del corrent de sortida

S'instal·la una resistència de referència entre el pin REXT i la terra, que controla la resistència interna de les sortides, vegeu el gràfic de la pàgina 9 del full de dades. Al DM634, aquesta resistència també es pot controlar mitjançant programari configurant la brillantor general (brillantor global); No entraré en detalls en aquest article, només posaré aquí una resistència de 2.2 - 3 kOhm.

Per entendre com controlar el xip, mirem la descripció de la interfície del dispositiu:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

Sí, aquí està, anglès xinès en tota la seva glòria. És problemàtic traduir això, podeu entendre-ho si voleu, però hi ha una altra manera: veure com es descriu la connexió al full de dades amb el TLC5940 tancat funcionalment:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
… Només calen tres pins per introduir dades al dispositiu. La vora ascendent del senyal SCLK desplaça les dades del pin SIN al registre intern. Després que s'hagin carregat totes les dades, un breu senyal XLAT alt tanca les dades transferides en sèrie als registres interns. Els registres interns són portes activades pel nivell de senyal XLAT. Totes les dades es transmeten primer MSB.

latch - pestell / pestell / pestell.
Bord ascendent és l'avantguarda del pols
MSB primer – el bit més significatiu (l'esquerra) endavant.
per registrar dades – Transmetre dades seqüencialment (bit a bit).

Paraula pestell sovint es troba a la documentació de xips i es tradueix de diverses maneres, així que per entendre'm ho permetré

petit programa educatiuEl controlador LED és essencialment un registre de desplaçament. "Majús" (canviar) al nom: moviment de dades bit a bit dins del dispositiu: cada nou bit introduït dins empeny tota la cadena cap endavant. Com que ningú vol observar el parpelleig caòtic dels LED durant el torn, el procés té lloc en registres de memòria intermèdia separats dels treballadors per una persiana (pestell) és una mena de vestidor on els bits s'alineen en la seqüència desitjada. Quan tot està a punt, la persiana s'obre i els bits es posen a treballar, substituint el lot anterior. Paraula pestell a la documentació de microcircuits gairebé sempre implica aquest amortidor, en qualsevol combinació que s'utilitzi.

Per tant, la transferència de dades al DM634 es realitza de la següent manera: establiu l'entrada DAI al valor del bit alt del LED llunyà, tireu DCK cap amunt i cap avall; establiu l'entrada DAI al valor del bit següent, tireu DCK; i així successivament fins que s'hagin transmès tots els bits (rellotge), després del qual tirem LAT. Això es pot fer manualmentpoc bang), però és millor utilitzar la interfície SPI especialment afinada per a això, ja que es presenta al nostre STM32 en dues còpies.

Tauleta blava STM32F103

Introducció: els controladors STM32 són molt més complicats que Atmega328 del que poden fer por. Al mateix temps, per raons d'estalvi d'energia, gairebé tots els perifèrics estan desactivats al principi i la freqüència de rellotge és de 8 MHz des d'una font interna. Afortunadament, els programadors de l'STM van escriure un codi que porta el xip als 72 MHz "calculats", i els autors de tots els IDE que conec l'han inclòs en el procediment d'inicialització, de manera que no hem de fer el rellotge (però pots si realment vols). Però cal encendre els perifèrics.

Documentació: el popular xip STM32F103C8T6 està instal·lat a Blue Pill, hi ha dos documents útils per a això:

A la fitxa tècnica, ens pot interessar:

  • Pinouts - pinouts de xip - per si decidim fer taules nosaltres mateixos;
  • Mapa de memòria: un mapa de memòria per a un xip específic. El Manual de referència té un mapa per a tota la línia, esmenta registres que no són al nostre.
  • Taula de definicions de pins: llista les funcions de pins principals i alternatives; per a la "píndola blava" a Internet, podeu trobar imatges més convenients amb una llista de pins i les seves funcions. Per tant, de seguida busquem a Google Blue Pill pinout i tenim aquesta imatge a mà:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
NB: hi ha hagut un error a la imatge d'Internet, observat als comentaris, pel qual gràcies. La imatge s'ha substituït, però aquesta és una lliçó: és millor comprovar la informació no de les fitxes.

Traiem el full de dades, obrim el Manual de referència, a partir d'ara només el fem servir.
Procediment: tractar l'entrada/sortida estàndard, configurar SPI, encendre els perifèrics necessaris.

D'entrada i sortida

A l'Atmega328, l'E/S és extremadament senzilla, per això l'abundància d'opcions STM32 pot ser confusa. Ara només necessitem conclusions, però fins i tot hi ha quatre opcions:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
sortida de drenatge obert, sortida push-pull, alternativa push-pull, alternativa de drenatge obert

"Tirar-empènyer" (push-pull) - la sortida habitual de l'Arduino, el pin pot ser ALTA o BAIXA. Però amb el "desguàs obert" sorgeixen dificultats, encara que de fet tot és senzill aquí:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Configuració de sortida / quan el port s'assigna a la sortida: / buffer de sortida habilitat: / – mode de drenatge obert: "0" al registre de sortida habilita N-MOS, "1" al registre de sortida deixa el port en mode Hi-Z (P -MOS no està activat ) / - Mode push-pull: "0" al registre de sortida activa N-MOS, "1" al registre de sortida activa P-MOS.

Tota la diferència de drenatge obert (desguàs obert) de "empènyer-tirar" (push-pull) és que al primer pin no pot prendre l'estat ALTA: quan s'escriu una unitat al registre de sortida, passa al mode d'alta resistència (alta impedància, Hola-Z). Quan escriu zero, el pin en ambdós modes es comporta igual, tant lògicament com elèctricament.

En el mode de sortida normal, el pin simplement tradueix el contingut del registre de sortida. En "alternativa" està controlat pel perifèric corresponent (vegeu 9.1.4):

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Si el bit del port es configura com a sortida de funció alternativa, el registre de sortida es desactiva i el pin es connecta al senyal de sortida del perifèric.

La funcionalitat alternativa de cada pin es descriu a Definicions de pins El full de dades es troba a la imatge descarregada. Quan se li pregunta què fer si el pin té diverses funcions alternatives, la resposta ve donada per una nota a peu de pàgina al full de dades:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Si diversos perifèrics utilitzen el mateix pin, per evitar conflictes entre funcions alternatives, només s'ha d'utilitzar un perifèric alhora, canviant mitjançant el bit d'habilitació del rellotge perifèric (al registre RCC corresponent).

Finalment, els pins en mode de sortida també tenen una velocitat de rellotge. Aquesta és una altra característica d'estalvi d'energia, en el nostre cas només la posem al màxim i l'oblidem.

Per tant: estem utilitzant SPI, el que significa que dos pins (amb dades i amb un senyal de rellotge) haurien de ser "funció push-pull alternativa", i un més (LAT) hauria de ser "push-pull normal". Però abans d'assignar-los, parlem de SPI.

SCI

Un altre petit truc

SPI o Serial Peripheral Interface (interfície perifèrica sèrie) és una interfície senzilla i molt eficaç per comunicar MK amb altres MK i amb el món exterior en general. El principi del seu funcionament ja s'ha descrit anteriorment, pel que fa al controlador LED xinès (vegeu la secció 25 del manual de referència). SPI pot funcionar en modes mestre ("mestre") i esclau ("esclau"). SPI té quatre canals bàsics, dels quals no tots poden estar implicats:

  • MOSI, sortida mestre / entrada esclau: aquest pin envia dades en mode mestre i rep dades en mode esclau;
  • MISO, Master Input / Slave Output: al contrari, en el mestre que rep, en l'esclau que dóna;
  • SCK, Serial Clock: estableix la freqüència de transmissió de dades al mestre o rep un senyal de rellotge a l'esclau. Essencialment, beats the beats;
  • SS, Slave Select: amb aquest canal, l'esclau sap que vol alguna cosa d'ell. A STM32 s'anomena NSS, on N = negatiu, és a dir. el controlador es converteix en esclau si aquest canal té terra. Combina bé amb el mode Open Drain Output, però aquesta és una altra història.

Com tota la resta, SPI a STM32 és ric en funcionalitats, cosa que fa que sigui una mica difícil d'entendre. Per exemple, pot funcionar no només amb SPI, sinó també amb una interfície I2S, i les seves descripcions es barregen a la documentació, cal tallar l'excés de manera oportuna. La nostra tasca és extremadament senzilla: només cal donar dades utilitzant només MOSI i SCK. Anem a l'apartat 25.3.4 (comunicació semidúplex, comunicació semidúplex), on trobem 1 rellotge i 1 cable de dades unidireccional (1 rellotge i 1 flux de dades unidireccional):

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
En aquest mode, l'aplicació utilitza SPI en mode només de transmissió o només de recepció. / El mode només de transmissió és similar al mode dúplex: les dades es transmeten al pin de transmissió (MOSI en mode mestre o MISO en mode esclau), mentre que el pin de recepció (MISO o MOSI respectivament) es pot utilitzar com a E/S normal. pin. En aquest cas, n'hi ha prou que l'aplicació ignori el buffer Rx (si es llegeix, no hi haurà dades transmeses).

Genial, el pin MISO és lliure, connectem-hi el senyal LAT. Anem a tractar amb Slave Select, que es pot controlar mitjançant programació a STM32, que és molt convenient. Llegim el paràgraf del mateix nom a l'apartat 25.3.1 Descripció general de l'SPI:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Control de programari NSS (SSM = 1) / La informació de selecció d'esclaus es troba al bit SSI del registre SPI_CR1. El pin NSS extern es deixa lliure per a altres necessitats d'aplicació.

És hora d'escriure als registres. Vaig decidir utilitzar SPI2, estem buscant la seva adreça base al full de dades, a la secció 3.3 Mapa de memòria (Mapa de memòria):

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

Bé, comencem:

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

Obrim la secció 25.3.3 amb el títol "Configuració de SPI en mode mestre":

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

1. Estableix el rellotge de la interfície sèrie amb els bits BR[2:0] al registre SPI_CR1.

Els registres es recullen a la secció del manual de referència del mateix nom. Canvi d'adreça (desplaçament d'adreça) CR1 té 0x00, per defecte s'esborren tots els bits (Restableix el valor 0x0000):

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

Els bits BR configuren el divisor de rellotge del controlador, determinant així la freqüència a la qual funcionarà l'SPI. La freqüència STM32 serà de 72 MHz, el controlador LED, segons el seu full de dades, funciona a una freqüència de fins a 25 MHz, per la qual cosa hem de dividir per quatre (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. Configureu els bits CPOL i CPHA per definir la relació entre la transferència de dades i el rellotge de la interfície sèrie (vegeu el diagrama a la pàgina 240)

Com que estem llegint un full de dades aquí, i no mirant esquemes, mirem més de prop la descripció textual dels bits CPOL i CPHA a la pàgina 704 (Descripció general de l'SPI):

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Fase i polaritat del rellotge
Utilitzant els bits CPOL i CPHA del registre SPI_CR1, podeu seleccionar programadament quatre opcions per a les relacions de temporització. El bit CPOL (polaritat del rellotge) controla l'estat del senyal del rellotge quan no es transmeten dades. Aquest bit controla els modes mestre i esclau. Si es restableix CPOL, el pin SCK està baix en repòs. Si el bit CPOL està configurat, el pin SCK és alt quan està inactiu.
Si el bit CPHA (Clock Phase) està establert, l'estroboscopi de trampa MSB és la segona vora del senyal SCK (caiguda si s'esborra CPOL, o front ascendent si CPOL està establert). Les dades es mantenen en el segon canvi de rellotge. Si s'esborra el bit CPHA, l'estroboscopi de trampa de bits alts és el front ascendent del senyal SCK (caiguda si s'estableix CPOL, o front ascendent si CPOL està clar). Les dades es mantenen en el primer canvi de rellotge.

Havent tastat aquest coneixement, arribem a la conclusió que tots dos bits han de romandre zero, perquè volem que el senyal SCK es mantingui baix quan no s'utilitzi i que les dades es transmetin al front ascendent del pols (vegeu la fig. Bord ascendent al full de dades DM634).

Per cert, aquí ens vam trobar per primera vegada amb una característica del vocabulari a les fitxes de dades ST: en elles s'escriu la frase "restableix el bit a zero" per reiniciar una micaI no per aclarir una mica, com, per exemple, Atmega.

3. Estableix el bit DFF per determinar el format del bloc de dades de 8 o 16 bits

He agafat específicament el DM16 de 634 bits per no molestar-me amb la transferència de dades PWM de 12 bits, com el DM633. DFF té sentit posar la unitat:

#define DFF         0x0800

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

4. Configureu el bit LSBFIRST al registre SPI_CR1 per definir el format de bloc

LSBFIRST, com el seu nom indica, configura primer la transmissió amb el bit menys significatiu. Però el DM634 vol rebre dades MSB primer. Per tant, ho deixem reset.

5. En el mode de maquinari, si cal l'entrada del pin NSS, conduïu el pin NSS alt durant tota la seqüència de transferència de bytes. En el mode de programa NSS, establiu els bits SSM i SSI al registre SPI_CR1. Si s'ha de sortir el pin NSS, només cal configurar el bit SSOE.

Instal·leu SSM i SSI per oblidar-vos del mode de maquinari NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. S'han d'establir els bits MSTR i SPE (només es mantenen si el NSS és alt)

De fet, amb aquests bits assignem el nostre SPI com a mestre i l'activem:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI està configurat, escrivim immediatament funcions que enviïn bytes al controlador. Continueu llegint 25.3.3 "Configuració de SPI en mode mestre":

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Ordre de transferència de dades
La transferència comença quan s'escriu un byte al buffer Tx.
El byte de dades es carrega al registre de desplaçament a paral·lel mode (des del bus intern) durant la transmissió del primer bit, després del qual es transmet consistent Mode pin MOSI, primer o últim bit cap endavant en funció de la configuració del bit LSBFIRST al registre CPI_CR1. La marca TXE s'estableix després de la transmissió de dades de la memòria intermèdia Tx al registre de canvi, i es genera una interrupció si s'estableix el bit TXEIE del registre CPI_CR1.

He destacat algunes paraules a la traducció per cridar l'atenció sobre una característica de la implementació de SPI als controladors STM. A l'Atmega, la bandera TXE (Tx buit, Tx està buit i llest per rebre dades) només s'estableix després que s'hagi enviat tot el byte cap a fora. I aquí aquesta bandera s'estableix després que el byte s'hagi introduït al registre de desplaçament intern. Com que s'introdueix allà amb tots els bits al mateix temps (en paral·lel), i després les dades es transmeten seqüencialment, el TXE s'estableix abans que el byte s'enviï completament. Això és important perquè en el cas del nostre controlador LED, hem de treure el pin LAT després de l'enviament Tot dades, és a dir. només la bandera TXE no ens serà suficient.

Això vol dir que necessitem una altra bandera. Vegem la 25.3.7 - "Marques d'estat":

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
<...>
Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Bandera OCUPADA
El maquinari estableix i esborra el senyalador BSY (escriure-hi no té cap efecte). La bandera BSY indica l'estat de la capa de comunicació SPI.
Es reinicia:
quan s'hagi completat la transferència (excepte en mode mestre si la transferència és contínua)
quan SPI està desactivat
quan es produeix un error de mode mestre (MODF=1)
Si la transmissió no és contínua, el senyalador BSY s'esborra entre cada transmissió de dades.

D'acord, serà útil. Esbrineu on es troba el buffer Tx. Per fer-ho, llegiu el "Registre de dades SPI":

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Bits 15:0 DR[15:0] Registre de dades
Dades rebudes o dades a transmetre.
El registre de dades es divideix en dos buffers, un d'escriptura (búfer de transmissió) i un altre de lectura (búfer de recepció). Una escriptura al registre de dades escriu a la memòria intermèdia Tx, i una lectura des del registre de dades retornarà el valor contingut a la memòria intermèdia Rx.

Bé, el registre d'estat, on hi ha senyals TXE i BSY:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

Nosaltres escrivim:

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

Bé, com que necessitem transferir 16 vegades dos bytes, segons el nombre de sortides del controlador LED, una cosa així:

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

Però encara no sabem com treure el pin LAT, així que tornem a l'E/S.

Assigna pins

A STM32F1, els registres responsables de l'estat dels pins són força inusuals. Està clar que n'hi ha més que Atmega, però també són diferents d'altres xips STM. Secció 9.1 Descripció general de GPIO:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Cadascun dels ports d'E/S de propòsit general (GPIO) té dos registres de configuració de 32 bits (GPIOx_CRL i GPIOx_CRH), dos registres de dades de 32 bits (GPIOx_IDR i GPIOx_ODR), un registre d'establiment/restabliment de 32 bits (GPIOx_BSRR), un registre de restabliment de 16 bits (GPIOx_BRR) i un registre de 32 bits. registre de bloqueig de bits (GPIOx_LCKR).

Els dos primers registres aquí són inusuals, i també bastant inconvenients, perquè els 16 pins del port estan escampats per ells en un format de "quatre bits per germà". Aquells. els pins del XNUMX al XNUMX es troben a CRL i la resta es troben a CRH. Al mateix temps, els registres restants s'ajusten correctament als bits de tots els pins del port, sovint quedant la meitat "reservats".

Per simplificar, comencem pel final de la llista.

No necessitem un registre de bloqueig.

Els registres de configuració i restabliment són bastant divertits, ja que es dupliquen parcialment entre si: només podeu escriure-ho tot en BSRR, on els 16 bits superiors restabliran el pin a zero i els inferiors es posaran a 1, o també podeu utilitzeu BRR, els 16 bits inferiors dels quals només restabliten el pin . M'agrada la segona opció. Aquests registres són importants perquè proporcionen accés atòmic als pins:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Instal·lació o restabliment atòmic
No cal que desactiveu les interrupcions quan programeu GPIOx_ODR a nivell de bits: podeu canviar un o més bits amb una única operació d'escriptura atòmica APB2. Això s'aconsegueix escrivint un "1" al registre set/reset (GPIOx_BSRR o, només per a reiniciar, GPIOx_BRR) del bit que s'ha de canviar. Els altres bits romandran sense canvis.

Els registres de dades tenen noms bastant parlants - IDR = entrada Registre de direcció, registre d'entrada; ODR= sortida Registre de direcció, registre de sortida. En el projecte actual, no els necessitem.

I finalment, els registres de control. Com que estem interessats en els pins del segon SPI, és a dir, PB13, PB14 i PB15, de seguida mirem CRH:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

I veiem que caldrà escriure alguna cosa a trossets del 20 al 31.

Ja hem descobert què volem dels pins anteriors, així que aquí faré sense cap captura de pantalla, només digues que MODE estableix la direcció (entrada si els dos bits estan configurats a 0) i la velocitat del pin (necessitem 50MHz, és a dir. tots dos pins a "1"), i CNF estableix el mode: normal "push-push" - 00, "alternatiu" - ​​10. Per defecte, com podem veure més amunt, tots els pins tenen el tercer bit des de la part inferior ( CNF0), els posa en mode entrada flotant.

Com que penso fer alguna cosa més amb aquest xip, per simplificar, generalment he definit tots els valors MODE i CNF possibles tant per als registres de control inferior com superior.

D'alguna manera així

#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

Els nostres pins es troben al port B (adreça base - 0x40010C00), codi:

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

I, en conseqüència, podeu escriure definicions per a LAT, que canviaran els registres BRR i 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 només per inèrcia, d'alguna manera sempre ho va ser, deixa que es quedi per tu)

Ara tot està genial, simplement no funciona. Com que es tracta de STM32, aquí estalvien electricitat, la qual cosa significa que cal activar el rellotge dels perifèrics necessaris.

Activa el rellotge

El rellotge és el responsable del rellotge, també són el rellotge. I ja vam poder notar l'abreviatura RCC. Ho busquem a la documentació: es tracta de Reset and Clock Control (Gestió de reset i clock).

Com s'ha dit més amunt, afortunadament, la gent de STM ens va fer la part més difícil del tema del cronometratge, per la qual cosa a ells moltes gràcies (de nou us donaré un enllaç a Lloc web de Di Haltper deixar clar el confós que està). Només necessitem registres responsables d'habilitar el rellotge perifèric (Peripheral Clock Enable Registers). Primer, busquem l'adreça base del RCC, és al principi de la "Targeta de memòria":

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

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

I després feu clic a l'enllaç on intentar trobar alguna cosa a la taula o, molt millor, repasseu les descripcions dels registres inclosos de les seccions sobre habilitar els registres. On trobem RCC_APB1ENR i RCC_APB2ENR:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

I en ells, respectivament, bits que inclouen el clock de SPI2, IOPB (I/O Port B) i funcions alternatives (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;

El codi final es pot trobar aquí.

Si hi ha una oportunitat i ganes de provar, connectem el DM634 així: DAI a PB15, DCK a PB13, LAT a PB14. Alimentem el conductor des de 5 volts, no us oblideu de combinar els terrenys.

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

STM8 PWM

PWM a STM8

Quan estava planejant aquest article, vaig decidir, per exemple, intentar dominar alguna funcionalitat d'un xip desconegut només amb l'ajuda d'un full de dades, de manera que un sabater no sortiria sense botes. STM8 era perfecte per a aquest paper: en primer lloc, tenia un parell de taulers xinesos amb STM8S103 i, en segon lloc, no és molt popular i, per tant, la temptació de llegir i trobar una solució a Internet es basa en l'absència d'aquestes mateixes solucions.

El xip també en té fitxa de dades и manual de referència RM0016, al primer pinout i adreces de registre, al segon, tota la resta. Programant STM8 en C en un IDE lleig ST Desenvolupament Visual.

Rellotge i E/S

Per defecte, STM8 funciona a una freqüència de 2 MHz, això s'ha de corregir immediatament.

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Rellotge HSI (alt intern)
El rellotge HSI es deriva d'un oscil·lador RC intern de 16 MHz amb un divisor programable (1 a 8). S'estableix al registre divisor del rellotge (CLK_CKDIVR).
Nota: l'oscil·lador HSI RC amb un divisor de 8 es selecciona com a font de rellotge mestre a l'inici.

Trobem l'adreça del registre al full de dades, la descripció a refman i veiem que cal esborrar el registre:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Com que executarem PWM i connectarem LED, mirem el pinout:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

El xip és petit, moltes funcions estan suspeses als mateixos pins. El que està entre claudàtors és "funcionalitat alternativa", es canvia per "bytes d'opció" (bytes d'opció) - una cosa com els fusibles Atmega. Podeu canviar els seus valors de manera programada, però no és necessari, perquè. La nova funcionalitat només s'activa després d'un reinici. És més fàcil utilitzar ST Visual Programmer (descarregat amb Visual Develop), que pot canviar aquests bytes. El pinout mostra que les sortides CH1 i CH2 del primer temporitzador estan amagades entre claudàtors; cal configurar els bits AFR1 i AFR0 a STVP, i el segon també transferirà la sortida CH1 del segon temporitzador de PD4 a PC5.

Així, 6 pins controlaran els LED: PC6, PC7 i PC3 per al primer temporitzador, PC5, PD3 i PA3 per al segon.

Configurar els pins d'E/S a l'STM8 és més senzill i lògic que a l'STM32:

  • Registre de direcció de dades familiar Atmega DDR (Registre de direcció de dades): 1 = sortida;
  • el primer registre de control CR1, quan surt, estableix el mode push-pull (1) o drenatge obert (0); com que connecto els LED al xip amb càtodes, aquí deixo zeros;
  • el segon registre de control CR2 estableix la velocitat de rellotge en sortir: 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

Configuració PWM

Primer, definim els termes:

  • Freqüència PWM – freqüència amb què el temporitzador funciona;
  • Recàrrega automàtica, AR – valor carregat automàticament, fins al qual comptarà el temporitzador (període de pols);
  • Esdeveniment d'actualització, UEV – un esdeveniment que es produeix quan el temporitzador ha comptat fins a AR;
  • Cicle de treball PWM - Cicle de treball PWM, sovint anomenat "cicle de treball";
  • Captura/Compara el valor – valor per capturar/comparar, comptant fins al qual el temporitzador farà alguna cosa (en el cas de PWM, inverteix el senyal de sortida);
  • valor de precàrrega - valor precarregat. comparar el valor no pot canviar mentre el temporitzador està en marxa, en cas contrari el cicle PWM es trencarà. Per tant, els nous valors transmesos es col·loquen a la memòria intermèdia i es treuen quan el temporitzador arriba al final del compte enrere i es reinicia;
  • Alineat amb les vores и Modes alineats al centre – alineació a la vora i al centre, igual que atmelovskie PWM ràpid и PWM de fase correcta.
  • OCiREF, senyal de referència de comparació de sortida - el senyal de sortida de referència, de fet, el que apareix al pin corresponent en mode PWM.

Com ja queda clar a partir del pinout, dos temporitzadors tenen capacitats PWM: el primer i el segon. Tots dos són de 16 bits, el primer té moltes funcions addicionals (en particular, pot comptar tant amunt com a baix). Necessitem que tots dos funcionin de la mateixa manera, així que vaig decidir començar pel segon, òbviament, més pobre, per no utilitzar accidentalment quelcom que no hi és. Algun problema és que la descripció de la funcionalitat PWM de tots els temporitzadors al manual de referència es troba al capítol sobre el primer temporitzador (mode 17.5.7 PWM), de manera que heu de saltar cap endavant i cap enrere pel document tot el temps.

PWM a STM8 té un avantatge important sobre Atmega PWM:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
PWM amb alineació de vores
Configuració del compte de baix a dalt
El recompte està actiu si el bit DIR del registre TIM_CR1 està clar
Exemple
L'exemple utilitza el primer mode PWM. El senyal de referència PWM OCiREF es manté alt mentre TIM1_CNT < TIM1_CCRi. En cas contrari, es necessita un nivell baix. Si el valor a comparar al registre TIM1_CCRi és més gran que el valor de càrrega automàtica (registre TIM1_ARR), el senyal OCiREF es manté a 1. Si el valor de comparació és 0, OCiREF es manté a zero....

Temporitzador STM8 durant esdeveniment d'actualització comprova primer comparar el valor, i només llavors produeix un senyal de referència. A Atmega, el temporitzador primer tremola, i després es compara, com a resultat, quan compare value == 0 la sortida és una agulla que s'ha de tractar d'alguna manera (per exemple, invertint la lògica programadament).

Aleshores, què volem fer: PWM de 8 bits (AR == 255), comptant de baix a dalt, alineació al llarg de la vora. Com que les bombetes estan connectades al xip mitjançant càtodes, el PWM hauria de sortir 0 (LED encès) fins que comparar el valor i 1 després.

Ja n'hem llegit sobre alguns Mode PWM, així trobem el registre desitjat del segon temporitzador cercant al manual de referència aquesta frase (18.6.8 - TIMx_CCMR1):

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
110: Primer mode PWM: quan es compta de baix a dalt, el primer canal està actiu sempre que TIMx_CNT < TIMx_CCR1. En cas contrari, el primer canal està inactiu. [més al document, còpia-enganxa errònia del temporitzador 1] 111: Segon mode PWM: quan es compta de baix a dalt, el primer canal està inactiu fins que TIMx_CNT < TIMx_CCR1. En cas contrari, el primer canal està actiu.

Com que els LED estan connectats al MK amb càtodes, el segon mode ens convé (el primer també, però això encara no ho sabem).

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Bit 3 OC1PE: Habilita la sortida de precàrrega 1
0: Registre de precàrrega a TIMx_CCR1 desactivat. Podeu escriure a TIMx_CCR1 en qualsevol moment. El nou valor funciona immediatament.
1: Registre de precàrrega a TIMx_CCR1 habilitat. Les operacions de lectura/escriptura accedeixen al registre de precàrrega. El valor precarregat de TIMx_CCR1 es carrega al registre d'ombra durant cada esdeveniment d'actualització.
*Nota: els registres de precàrrega s'han d'habilitar perquè el mode PWM funcioni correctament. Això és opcional en mode de senyal únic (el bit OPM s'estableix al registre TIMx_CR1).

D'acord, activeu tot el que necessiteu per als tres canals del segon temporitzador:

#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 consta de dos registres de vuit bits, aquí tot és senzill:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

El segon temporitzador només pot comptar de baix a dalt, alineació a la vora, no cal canviar res. Estableix el divisor de freqüència, per exemple, a 256. Per al segon temporitzador, el divisor s'estableix al registre TIM2_PSCR i té una potència de dos:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Queda per activar les conclusions i el segon temporitzador en si. La primera tasca es resol mitjançant registres Captura/Compara Enable: n'hi ha dos, tres canals estan escampats asimètricament per sobre d'ells. Aquí també podem aprendre que és possible canviar la polaritat del senyal, és a dir. en principi, també es podria utilitzar el mode PWM 1. Escrivim:

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

I finalment, iniciem el temporitzador al registre TIMx_CR1:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Escrivim un anàleg senzill d'AnalogWrite (), que passarà els valors reals al temporitzador per a la seva comparació. Els registres tenen un nom previsible Captura/Compara registres, n'hi ha dos per a cada canal: els 8 bits baixos a TIM2_CCRxL i els bits alts a TIM2_CCRxH. Com que vam començar PWM de 8 bits, n'hi ha prou amb escriure només els bits baixos:

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

El lector atent notarà que tenim un PWM lleugerament defectuós, incapaç d'omplir el 100% (a un valor màxim de 255, el senyal s'inverteix per un cicle de temporitzador). Per als LED, això no té cap paper, i el lector atent ja endevina com solucionar-ho.

PWM al segon temporitzador funciona, aneu al primer.

El primer temporitzador té exactament els mateixos bits en els mateixos registres (és que els bits que van quedar "reservats" en el segon temporitzador s'utilitzen activament per a tot tipus de coses avançades en el primer). Per tant, n'hi ha prou amb trobar les adreces dels mateixos registres al full de dades i copiar el codi. Bé, canvieu el valor del divisor de freqüència, perquè. el primer temporitzador no vol obtenir una potència de dos, sinó un valor exacte de 16 bits en dos registres Preescalador alt и Sota. Ho fem de tot i... el primer temporitzador no funciona. Què passa?

L'única manera de resoldre el problema és mirant tota la secció sobre els registres de control del temporitzador 1, on estem buscant un que el segon temporitzador no té. hi haurà 17.7.30 Pausa registre (TIM1_BKR), on hi ha una mica com això:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Activa la sortida principal

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Això és tot per ara, el codi allà.

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

STM8 Multiplex

Multiplexació a STM8

El tercer mini-projecte consisteix a connectar vuit LED RGB al segon temporitzador en mode PWM i fer que mostrin diferents colors. Es basa en el concepte de multiplexació de LED, que consisteix en que si s'encenen i apaguen els LED molt i molt ràpidament, ens semblarà que estan enceses constantment (persistència de la visió, inèrcia de la percepció visual). Una vegada ho vaig fer alguna cosa així a l'arduino.

L'algorisme de treball té aquest aspecte:

  • connectat l'ànode del primer LED RGB;
  • encendre-lo donant els senyals necessaris als càtodes;
  • va esperar el final del cicle PWM;
  • connectat l'ànode del segon LED RGB;
  • l'enceneu...

Bé, etc. Per descomptat, per a un treball bonic, cal que la connexió de l'ànode i l'"encesa" del LED es produeixin simultàniament. Bé, gairebé. En qualsevol cas, hem d'escriure un codi que sortirà valors en tres canals del segon temporitzador, canviar-los quan s'arribi a la UEV i, alhora, canviar el LED RGB actiu actualment.

Com que el canvi de LED és automàtic, heu de crear "memòria de vídeo" des d'on el gestor d'interrupcions rebrà dades. Aquesta és una matriu senzilla:

uint8_t colors[8][3];

Per canviar el color d'un LED determinat, n'hi haurà prou amb escriure els valors necessaris en aquesta matriu. I la variable serà responsable del nombre del LED actiu

uint8_t cnt;

Demux

Per a una multiplexació adequada, necessitem, curiosament, el demultiplexor CD74HC238. Demultiplexer - un xip que implementa l'operador en el maquinari <<. A través de tres pins d'entrada (bits 0, 1 i 2) li alimentem un número X de tres bits, i en resposta activa el número de sortida (1<<X). Les entrades restants del xip s'utilitzen per escalar tot el disseny. Necessitem aquest xip no només per reduir el nombre de pins ocupats del microcontrolador, sinó també per seguretat, per no encendre accidentalment més LED del possible i no cremar el MK. El xip costa un cèntim, s'ha de guardar sempre a la farmaciola de casa.

CD74HC238 serà l'encarregat de subministrar tensió a l'ànode del LED desitjat. En un múltiplex complet, subministraria tensió a la columna a través del P-MOSFET, però en aquesta demostració, ho podeu fer directament, perquè. tira 20mA, segons qualificacions màximes absolutes al full de dades. Des de Full de dades CD74HC238 necessitem un pinout i aquest full de trucs:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
H = nivell d'alta tensió, L = nivell de baixa tensió, X - no m'importa

Connectem E2 i E1 a terra, E3, A0, A1 i A3 als pins PD5, PC3, PC4 i PC5 de STM8. Com que la taula anterior conté nivells alts i baixos, hem configurat aquests pins com a pins push-pull.

PWM

El PWM del segon temporitzador es configura de la mateixa manera que a la història anterior, amb dues diferències:

En primer lloc, hem d'activar la interrupció Esdeveniment d'actualització (UEV) que cridarà una funció per canviar el LED actiu. Això es fa canviant el bit Actualitzar la interrupció activada en un registre amb un nom parlant

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
Interromp el registre d'habilitació

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

La segona diferència està relacionada amb un fenomen de multiplexació com l'efecte fantasma - resplendor paràsit dels díodes. En el nostre cas, pot semblar que el temporitzador, després d'haver provocat una interrupció a l'UEV, continua funcionant i el controlador d'interrupcions no té temps per canviar el LED abans que el temporitzador comenci a escriure alguna cosa a les sortides. Per combatre-ho, haureu d'invertir la lògica (0 = brillantor màxima, 255 = no hi ha res activat) i no permetre valors de cicle de treball extrems. Aquells. Assegureu-vos que després de la UEV els LED s'apaguin completament durant un cicle PWM.

Canvia la polaritat:

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

Eviteu establir r, g i b a 255 i recordeu invertir-los quan feu servir.

Interrupcions

L'essència de la interrupció és que, en determinades circumstàncies, el xip deixa d'executar el programa principal i crida alguna funció externa. Les interrupcions es produeixen a causa d'influències externes o internes, inclosa la del temporitzador.

Quan vam crear per primera vegada un projecte a ST Visual Develop, a part de main.c tenim una finestra amb un fitxer misteriós stm8_interrupt_vector.cautomàticament inclòs en el projecte. En aquest fitxer, s'adjunta una funció a cada interrupció NonHandledInterrupt. Hem d'enllaçar la nostra funció a la interrupció desitjada.

El full de dades té una taula de vectors d'interrupció, on trobem els que necessitem:

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8
13 Actualització/desbordament de TIM2
14 TIM2 Captura/Comparació

Hem de canviar el LED a UEV, de manera que cal la interrupció #13.

En conseqüència, en primer lloc, a l'expedient stm8_interrupt_vector.c canviar el nom de la funció responsable de la interrupció número 13 (IRQ13) per defecte pel nostre:

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

En segon lloc, haurem de crear un fitxer main.h contingut com aquest:

#ifndef __MAIN_H
#define __MAIN_H

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

I, finalment, escriviu aquesta funció al vostre 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;
}

Queda per habilitar les interrupcions. Això es fa amb una ordre assemblador. rim - l'hauràs de buscar Manual de programació:

//enable interrupts
_asm("rim");

Una altra instrucció del muntador - sim - Desactiva les interrupcions. S'han de desactivar mentre s'escriuen nous valors a la "memòria de vídeo" perquè una interrupció provocada en un moment desafortunat no faci malbé la matriu.

Tot el codi - a Github.

Llegiu els fulls de dades 2: SPI a STM32; PWM, temporitzadors i interrupcions a STM8

Si almenys algú aquest article és útil, no l'he escrit en va. Estaré encantat de fer comentaris i observacions, intentaré respondre-ho tot.

Font: www.habr.com

Afegeix comentari