Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

В a primeira parte Intentei dicir aos enxeñeiros electrónicos afeccionados que creceron a partir dos pantalóns Arduino como e por que deberían ler as follas de datos e outra documentación dos microcontroladores. O texto resultou ser grande, polo que prometín mostrar exemplos prácticos nun artigo separado. Ben, chamábase cogomelo de leite...

Hoxe mostrareivos como usar fichas técnicas para resolver tarefas bastante sinxelas, pero necesarias para moitos proxectos, nos controladores STM32 (Blue Pill) e STM8. Todos os proxectos de demostración están dedicados aos meus LED favoritos, iluminarémolos en grandes cantidades, para o que teremos que utilizar todo tipo de periféricos interesantes.

O texto volveu resultar enorme, así que por comodidade estou facendo o contido:

STM32 Blue Pill: 16 LED con controlador DM634
STM8: Configuración de seis pinos PWM
STM8: 8 LED RGB en tres pinos, interrupcións

Descargo de responsabilidade: non son enxeñeiro, non pretendo ter coñecementos profundos en electrónica, o artigo está destinado a afeccionados coma min. De feito, hai dous anos considerábame o público obxectivo. Se alguén me dixera entón que as follas de datos nun chip descoñecido non daban medo de ler, non pasaría moito tempo buscando algúns anacos de código en Internet e inventando muletas con tesoiras e cinta adhesiva.

O foco deste artigo está en follas de datos, non en proxectos, polo que o código pode non estar moi limpio e moitas veces limitado. Os proxectos en si son moi sinxelos, aínda que axeitados para un primeiro coñecemento do novo chip.

Espero que o meu artigo axude a alguén nunha fase similar de inmersión no hobby.

STM32

16 LED con DM634 e SPI

Un pequeno proxecto que utiliza Blue Pill (STM32F103C8T6) e o controlador LED DM634. Usando follas de datos, descubriremos o controlador, os portos STM IO e configuraremos SPI.

DM634

Chip taiwanés con 16 saídas PWM de 16 bits, pódese conectar en cadea. O modelo de gama baixa de 12 bits coñécese a partir dun proxecto doméstico Paquete de luz. Ao mesmo tempo, escollendo entre o DM63x e o coñecido TLC5940, escollín DM por varias razóns: 1) TLC en Aliexpress é definitivamente falso, pero este non o é; 2) DM ten un PWM autónomo co seu propio xerador de frecuencia; 3) podería mercarse de xeito económico en Moscova, en lugar de esperar por un paquete de Ali. E, por suposto, foi interesante aprender a controlar o chip vostede mesmo, en lugar de usar unha biblioteca preparada. Os chips agora preséntanse principalmente no paquete SSOP24; son fáciles de soldar a un adaptador.

Dado que o fabricante é taiwanés, folla de datos o chip está escrito en inglés chinés, o que significa que será divertido. Primeiro miramos o pinout (Conexión pin) para entender a que pata conectar que e unha descrición dos pinos (Descrición do pin). 16 pinos:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Fontes de sumidoiros de CC (desagüe aberto)

Fregadero / Saída de drenaxe aberto - drenar; fonte de corrente entrante; a saída está conectada a terra no estado activo: os LED están conectados ao controlador mediante cátodos. Eléctricamente, isto non é, por suposto, un "desagüe aberto" (desaugadoiro aberto), pero nas follas de datos adoita atoparse esta designación para pins en modo de drenaxe.

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Resistencias externas entre REXT e GND para establecer o valor da corrente de saída

Instálase unha resistencia de referencia entre o pin REXT e a terra, que controla a resistencia interna das saídas, consulte o gráfico da páxina 9 da folla de datos. No DM634, esta resistencia tamén se pode controlar mediante software, configurando o brillo global (brillo global); Non entrarei en detalles neste artigo, só poñerei aquí unha resistencia de 2.2 - 3 kOhm.

Para entender como controlar o chip, vexamos a descrición da interface do dispositivo:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

Si, aquí está, inglés chinés en todo o seu esplendor. Traducir isto é problemático, podes entendelo se o desexas, pero hai outra forma: mira como se describe a conexión co TLC5940 funcionalmente similar na folla de datos:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
... Só son necesarios tres pinos para introducir datos no dispositivo. O bordo ascendente do sinal SCLK despraza os datos do pin SIN ao rexistro interno. Despois de cargar todos os datos, un breve sinal XLAT alto engancha os datos transferidos secuencialmente nos rexistros internos. Os rexistros internos son portas activadas polo nivel de sinal XLAT. Todos os datos transmítense primeiro o bit máis significativo.

Peche - pestillo / pestillo / pechadura.
Borde ascendente - borde de ataque do pulso
MSB primeiro – bit máis significativo (máis á esquerda) adiante.
para rexistrar datos – transmitir datos secuencialmente (bit a bit).

Palabra trinco atópase a miúdo na documentación de fichas e tradúcese de varias maneiras, polo que, para entendelo, voume permitir

un pequeno programa educativoO controlador LED é esencialmente un rexistro de desprazamento. "Maiús" (desprazarse) no nome: movemento bit a bit dos datos dentro do dispositivo: cada novo bit introducido dentro empuxa a cadea enteira diante del. Dado que ninguén quere observar o parpadeo caótico dos LED durante a quenda, o proceso ten lugar en rexistros tampón separados dos rexistros de traballo por un amortiguador (trinco) é unha especie de sala de espera onde os bits están dispostos na secuencia desexada. Cando todo está listo, a persiana ábrese e os anacos van a traballar, substituíndo o lote anterior. Palabra trinco na documentación dos microcircuítos case sempre implica tal amortecedor, sen importar en que combinacións se use.

Entón, a transferencia de datos ao DM634 realízase así: establece a entrada DAI no valor do bit máis significativo do LED afastado, tira DCK cara arriba e abaixo; establecer a entrada DAI co valor do seguinte bit, tirar DCK; e así sucesivamente ata que todos os bits sexan transmitidos (marcado), despois de que tiramos LAT. Isto pódese facer manualmente (bit-bang), pero é mellor utilizar unha interface SPI especialmente adaptada para iso, xa que se presenta no noso STM32 en dúas copias.

Pílula azul STM32F103

Introdución: os controladores STM32 son moito máis complexos que Atmega328 do que poden parecer asustados. Ademais, por razóns de aforro enerxético, case todos os periféricos están apagados ao inicio e a frecuencia do reloxo é de 8 MHz da fonte interna. Afortunadamente, os programadores de STM escribiron código que leva o chip ata os 72 MHz "calculados" e os autores de todos os IDE que coñezo incluírono no procedemento de inicialización, polo que non necesitamos reloxo (pero podes se realmente queres). Pero terás que acender os periféricos.

Documentación: Blue Pill está equipado co popular chip STM32F103C8T6, hai dous documentos útiles para iso:

Na ficha técnica podemos estar interesados ​​en:

  • Pinouts - chip pinouts - por se decidimos facer as placas nós mesmos;
  • Mapa de memoria: mapa de memoria para un chip específico. O Manual de referencia ten un mapa para toda a liña, e menciona rexistros que non ten o noso.
  • Táboa de definicións de pinos: lista as funcións principais e alternativas dos pinos; para a "pílula azul" podes atopar imaxes máis convenientes en Internet cunha lista de pinos e as súas funcións. Polo tanto, buscamos inmediatamente en Google Blue Pill pinout e temos esta imaxe a man:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
NB: houbo un erro na imaxe de Internet, que se sinalou nos comentarios, grazas por iso. A imaxe foi substituída, pero esta é unha lección: é mellor comprobar a información non das follas de datos.

Eliminamos a folla de datos, abrimos o Manual de referencia e, a partir de agora, só o utilizamos.
Procedemento: tratámonos de entrada/saída estándar, configuramos SPI, acendemos os periféricos necesarios.

Entrada Saída

No Atmega328, a E/S implícase de xeito moi sinxelo, polo que a abundancia de opcións STM32 pode ser confusa. Agora só necesitamos conclusións, pero aínda estas teñen catro opcións:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
sumidoiro aberto, empurrar-pull, empurrar-pull alternativo, drenaxe aberto alternativo

"Pull-push" (empurrar-tirar) é a saída habitual do Arduino, o pin pode tomar o valor ALTO ou BAIXO. Pero con "drenaxe aberto" hai dificultades, aínda que de feito todo é sinxelo aquí:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Configuración de saída / cando o porto está asignado á saída: / búfer de saída activado: / – modo de drenaxe aberto: "0" no rexistro de saída activa N-MOS, "1" no rexistro de saída deixa o porto en modo Hi-Z ( P-MOS non está activado ) / – Modo push-pull: "0" no rexistro de saída activa N-MOS, "1" no rexistro de saída activa P-MOS.

Toda a diferenza entre o drenaxe aberto (desaugadoiro aberto) de "push-pull" (empurrar-tirar) é que no primeiro pin non pode aceptar o estado ALTO: ao escribir un no rexistro de saída, pasa ao modo de alta resistencia (impedancia elevada, Ola-Z). Cando se escribe cero, o pin compórtase igual en ambos os modos, tanto lóxica como eléctricamente.

No modo de saída normal, o pin simplemente transmite o contido do rexistro de saída. Na "alternativa" está controlada polos periféricos correspondentes (ver 9.1.4):

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Se un bit de porto está configurado como un pin de función alternativa, o rexistro de pin está desactivado e o pin está conectado ó pin periférico.

A funcionalidade alternativa de cada pin descríbese en Definicións de pin A folla de datos está na imaxe descargada. Á pregunta de que facer se un alfinete ten varias funcións alternativas, a resposta vén dada por unha nota ao pé na folla de datos:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Se varios periféricos usan o mesmo pin, para evitar conflitos entre funcións alternativas, só se debe usar un periférico á vez, alternado mediante o bit de activación do reloxo periférico (no rexistro RCC apropiado).

Finalmente, os pinos en modo de saída tamén teñen unha velocidade de reloxo. Esta é outra función de aforro enerxético; no noso caso, simplemente poñémola ao máximo e esquecémola.

Entón: estamos usando SPI, o que significa que dous pinos (con datos e cun sinal de reloxo) deberían ser "función push-pull alternativa", e outro (LAT) debería ser "push-pull regular". Pero antes de asignalos, imos tratar con SPI.

SPI

Outro pequeno programa educativo

SPI ou Serial Peripheral Interface (interfaz de periféricos en serie) é unha interface sinxela e moi eficaz para conectar un MK con outros MK e co mundo exterior en xeral. O principio do seu funcionamento xa se describiu anteriormente, onde se refire ao controlador LED chinés (no manual de referencia, consulte a sección 25). SPI pode funcionar en modo mestre ("mestre") e escravo ("escravo"). SPI ten catro canles básicas, das cales non todas se poden usar:

  • MOSI, Master Output / Slave Input: este pin transmite datos en modo mestre e recibe datos en modo escravo;
  • MISO, Master Input / Slave Output: pola contra, recibe no mestre, e transmite no escravo;
  • SCK, Serial Clock: establece a frecuencia de transmisión de datos no mestre ou recibe un sinal de reloxo no escravo. Esencialmente bater ritmos;
  • SS, Slave Select: coa axuda desta canle, o escravo sabe que se lle quere algo. En STM32 chámase NSS, onde N = negativo, é dicir. o controlador convértese nun escravo se hai terra nesta canle. Combínase ben co modo Open Drain Output, pero esa é outra historia.

Como todo o demais, SPI en STM32 é rico en funcións, o que fai que sexa algo difícil de entender. Por exemplo, pode funcionar non só con SPI, senón tamén cunha interface I2S, e na documentación as súas descricións están mesturadas, é necesario cortar o exceso de forma oportuna. A nosa tarefa é moi sinxela: só necesitamos enviar datos usando só MOSI e SCK. Imos ao apartado 25.3.4 (comunicación semidúplex, comunicación semidúplex), onde atopamos 1 reloxo e 1 cable de datos unidireccional (1 sinal de reloxo e 1 fluxo de datos unidireccional):

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Neste modo, a aplicación usa SPI en modo de só transmisión ou só de recepción. / O modo de só transmisión é semellante ao modo dúplex: os datos transmítense no pin de transmisión (MOSI en modo mestre ou MISO en modo escravo) e o pin de recepción (MISO ou MOSI respectivamente) pódese usar como pin de E/S normal. . Neste caso, a aplicación só necesita ignorar o búfer Rx (se é lido, alí non haberá datos transferidos).

Xenial, o pin MISO está libre, conectemos o sinal LAT a el. Vexamos Slave Select, que no STM32 pódese controlar mediante programación, o que é moi cómodo. Lemos o parágrafo do mesmo nome na sección 25.3.1 SPI Descrición xeral:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Control de software NSS (SSM = 1)/A información de selección de escravos está contida no bit SSI do rexistro SPI_CR1. O pin externo NSS permanece libre para outras necesidades da aplicación.

É hora de escribir aos rexistros. Decidín usar SPI2, busque o seu enderezo base na folla de datos - na sección 3.3 Mapa de memoria:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

Ben, imos comezar:

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

Abre a sección 25.3.3 co título autoexplicativo "Configuración de SPI no modo mestre":

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

1. Estableza a frecuencia do reloxo en serie cos bits BR[2:0] no rexistro SPI_CR1.

Os rexistros recóllense na sección do manual de referencia do mesmo nome. Cambio de enderezo (Desprazamento do enderezo) para CR1 – 0x00, por defecto todos os bits están borrados (Restablecer o valor 0x0000):

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

Os bits BR establecen o divisor de reloxo do controlador, determinando así a frecuencia á que operará o SPI. A nosa frecuencia STM32 será de 72 MHz, o controlador de LED, segundo a súa folla de datos, funciona cunha frecuencia de ata 25 MHz, polo que debemos dividir por catro (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. Configure os bits CPOL e CPHA para definir a relación entre a transferencia de datos e a temporización do reloxo en serie (consulte o diagrama na páxina 240)

Xa que estamos lendo unha folla de datos aquí e non mirando esquemas, vexamos máis de cerca a descrición do texto dos bits CPOL e CPHA na páxina 704 (Descrición xeral de SPI):

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Fase e polaridade do reloxo
Usando os bits CPOL e CPHA do rexistro SPI_CR1, pode seleccionar catro relacións de temporización mediante programación. O bit CPOL (polaridade do reloxo) controla o estado do sinal do reloxo cando non se transmiten datos. Este bit controla os modos mestre e escravo. Se CPOL se restablece, o pin SCK está baixo no modo de descanso. Se o bit CPOL está configurado, o pin SCK está alto durante o modo de descanso.
Cando o bit CPHA (fase de reloxo) está configurado, o estrobo de trampa de bits altos é o segundo bordo do sinal SCK (caendo se CPOL está claro, aumentando se CPOL está configurado). Os datos son capturados polo segundo cambio no sinal do reloxo. Se o bit CPHA está claro, a trampa estroboscópica de bit alto é o bordo ascendente do sinal SCK (fronte descendente se CPOL está configurado, fronte ascendente se CPOL está borrado). Os datos captúranse no primeiro cambio no sinal do reloxo.

Unha vez absorbido este coñecemento, chegamos á conclusión de que ambos os bits deben seguir sendo ceros, porque Queremos que o sinal SCK permaneza baixo cando non estea en uso e que os datos se transmitan no bordo ascendente do pulso (ver Fig. Borde ascendente na folla de datos DM634).

Por certo, aquí atopamos por primeira vez unha característica do vocabulario das follas de datos ST: nelas está escrita a frase "reset the bit to zero" para reiniciar un poucopero non para despexar un pouco, como, por exemplo, Atmega.

3. Establece o bit DFF para determinar se o bloque de datos ten o formato de 8 ou 16 bits

Tomei específicamente un DM16 de 634 bits para non molestarme en transmitir datos PWM de 12 bits, como o DM633. Ten sentido configurar DFF en un:

#define DFF         0x0800

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

4. Configure o bit LSBFIRST no rexistro SPI_CR1 para determinar o formato de bloque

LSBFIRST, como o seu nome indica, configura primeiro a transmisión co bit menos significativo. Pero DM634 quere recibir datos a partir do bit máis significativo. Polo tanto, deixámolo reiniciar.

5. No modo de hardware, se é necesaria a entrada do pin NSS, aplique un sinal alto ao pin NSS durante toda a secuencia de transferencia de bytes. No modo de software NSS, configure os bits SSM e SSI no rexistro SPI_CR1. Se o pin NSS se usa como saída, só se debe configurar o bit SSOE.

Instala SSM e SSI para esquecer o modo de hardware NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. Débense establecer os bits MSTR e SPE (quedan establecidos só se o sinal NSS é alto)

En realidade, con estes bits designamos o noso SPI como mestre e acendemos:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI está configurado, escribamos inmediatamente funcións que envíen bytes ao controlador. Continúa lendo 25.3.3 "Configuración de SPI en modo mestre":

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Orde de transferencia de datos
A transmisión comeza cando se escribe un byte no búfer Tx.
O byte de datos cárgase no rexistro de desprazamento en paralelo modo (desde o bus interno) durante a transmisión do primeiro bit, despois do cal se transmite a secuencial Modo PIN MOSI, primeiro ou último bit adiante dependendo da configuración do bit LSBFIRST no rexistro CPI_CR1. A bandeira TXE establécese despois da transmisión de datos do búfer Tx ao rexistro de desprazamento, e tamén xera unha interrupción se o bit TXEIE no rexistro CPI_CR1 está configurado.

Destaquei algunhas palabras na tradución para chamar a atención sobre unha característica da implementación SPI nos controladores STM. En Atmega a bandeira TXE (Tx baleiro, Tx está baleiro e listo para recibir datos) só se configura despois de enviar todo o byte cara a fóra. E aquí esta marca establécese despois de inserir o byte no rexistro de desprazamento interno. Dado que se empurra alí con todos os bits ao mesmo tempo (en paralelo) e despois os datos transfírense secuencialmente, o TXE establécese antes de que o byte se envíe por completo. Isto é importante porque no caso do noso controlador LED, necesitamos tirar do pin LAT despois do envío Todo datos, é dicir A bandeira de TXE por si soa non será suficiente para nós.

Isto significa que necesitamos outra bandeira. Vexamos 25.3.7 - "Baleiras de estado":

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
<...>
Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
bandeira BUSY
A bandeira BSY está establecida e borrada polo hardware (escribir nela non ten efecto). A bandeira BSY indica o estado da capa de comunicación SPI.
Restablece:
cando se complete a transferencia (excepto no modo mestre se a transferencia é continua)
cando SPI está desactivado
cando se produce un erro do modo mestre (MODF=1)
Se a transferencia non é continua, a bandeira BSY borrarase entre cada transferencia de datos

Vale, isto será útil. Imos descubrir onde está o buffer Tx. Para iso, lea "Rexistro de datos SPI":

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Bits 15:0 DR[15:0] Rexistro de datos
Datos recibidos ou datos a transmitir.
O rexistro de datos divídese en dous búfers: un para escribir (búfer de transmisión) e outro para ler (búfer de recepción). Ao escribir no rexistro de datos escríbese no búfer Tx, e a lectura do rexistro de datos devolverá o valor contido no búfer Rx.

Ben, e o rexistro de estado, onde se atopan as bandeiras TXE e BSY:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

Escribimos:

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

Ben, xa que necesitamos transmitir 16 veces dous bytes, segundo o número de saídas do controlador LED, algo así:

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

Pero aínda non sabemos como tirar do pin LAT, así que volveremos a E/S.

Asignación de pinos

No STM32F1, os rexistros responsables do estado dos pinos son bastante inusuales. Está claro que hai máis que Atmega, pero tamén son diferentes doutros chips STM. Sección 9.1 Descrición xeral do GPIO:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Cada un dos portos de E/S de propósito xeral (GPIO) ten dous rexistros de configuración de 32 bits (GPIOx_CRL e GPIOx_CRH), dous rexistros de datos de 32 bits (GPIOx_IDR e GPIOx_ODR), un rexistro de configuración/reinicio de 32 bits (GPIOx_BSRR), un rexistro de restablecemento de 16 bits (GPIOx_BRR) e un rexistro de 32 bits. rexistro de bloqueo de bits (GPIOx_LCKR).

Os dous primeiros rexistros son pouco comúns, e tamén bastante inconvenientes, porque os 16 pinos de porto están espallados por eles nun formato de "catro bits por irmán". Eses. os pinos de cero a sete están en CRL e o resto están en CRH. Ao mesmo tempo, os rexistros restantes conteñen con éxito os bits de todos os pinos do porto, a miúdo quedando a metade "reservados".

Para simplificar, imos comezar polo final da lista.

Non necesitamos un rexistro de bloqueo.

Os rexistros de axuste e restablecemento son bastante divertidos xa que se duplican parcialmente entre si: só podes escribir todo en BSRR, onde os 16 bits máis altos restablecerán o pin a cero e os inferiores a 1, ou tamén podes use BRR, cuxos 16 bits inferiores só restablecen o pin . Gústame a segunda opción. Estes rexistros son importantes porque proporcionan acceso atómico aos pinos:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Atomic Set ou Reset
Non é necesario desactivar as interrupcións ao programar GPIOx_ODR a nivel de bits: pódense cambiar un ou máis bits cunha única operación de escritura atómica APB2. Isto conséguese escribindo un "1" no rexistro de axuste/reinicio (GPIOx_BSRR ou, só para restablecer, GPIOx_BRR) do bit que se debe cambiar. Outros bits permanecerán sen cambios.

Os rexistros de datos teñen nomes bastante explicativos - IDR = Entrada Rexistro de dirección, rexistro de entrada; ODR = Saída Rexistro de dirección, rexistro de saída. Non os necesitaremos no proxecto actual.

E por último, rexistros de control. Dado que estamos interesados ​​nos segundos pinos SPI, é dicir, PB13, PB14 e PB15, miramos inmediatamente a CRH:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

E vemos que teremos que escribir algo en bits do 20 ao 31.

Xa descubrimos arriba o que queremos dos pinos, así que aquí farei sen unha captura de pantalla, só direi que MODE especifica a dirección (entrada se os dous bits están configurados en 0) e a velocidade do pin (necesitamos 50MHz, é dicir. ambos os pines en "1"), e CNF establece o modo: "push-pull" regular - 00, "alternativo" - 10. Por defecto, como vemos arriba, todos os pinos teñen o terceiro bit desde abaixo (CNF0), ponos en modo entrada flotante.

Xa que planeo facer outra cousa con este chip, por simplicidade definín todos os valores MODE e CNF posibles para os rexistros de control inferior e superior.

Dalgunha maneira así

#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

Os nosos pinos están situados no porto B (enderezo base - 0x40010C00), código:

#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, en consecuencia, podes escribir definicións para LAT, que serán alteradas polos rexistros 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 só por inercia, sempre foi así, deixalo quedar)

Agora todo está xenial, pero non funciona. Debido a que se trata de STM32, aforran electricidade, o que significa que cómpre activar o reloxo dos periféricos necesarios.

Activa o reloxo

O reloxo, tamén coñecido como Reloxo, é o responsable do reloxo. E xa puidemos notar a abreviatura RCC. Buscámolo na documentación: isto é Restablecer e Control do reloxo.

Como xa se dixo anteriormente, afortunadamente, a parte máis difícil do tema do cronometraxe fixéronos persoas de STM, polo que lles agradecemos moito (unha vez máis poñerei un enlace a Páxina web de Di Halt, para deixar claro o confuso que é). Só necesitamos rexistros responsables de habilitar o reloxo periférico (Peripheral Clock Enable Registers). Primeiro, imos atopar o enderezo base do RCC, que está ao comezo do "Mapa de memoria":

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

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

E despois fai clic na ligazón onde tentas atopar algo na placa, ou, moito mellor, percorre as descricións dos rexistros de habilitación das seccións sobre habilitar rexistros. Onde atoparemos RCC_APB1ENR e RCC_APB2ENR:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

E, en consecuencia, conteñen bits que inclúen a sincronización de SPI2, IOPB (I/O Port B) e funcións alternativas (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;

O código final pódese atopar aquí.

Se tes a oportunidade e o desexo de probar, conecta o DM634 deste xeito: DAI a PB15, DCK a PB13, LAT a PB14. Alimentamos o controlador desde 5 voltios, non esquezas conectar os terreos.

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

STM8 PWM

PWM en STM8

Cando estaba a planear este artigo, decidín, a modo de exemplo, tentar dominar algunha funcionalidade dun chip descoñecido usando só unha folla de datos, para non acabar cun zapateiro sen botas. STM8 era ideal para este papel: en primeiro lugar, tiña un par de placas chinesas con STM8S103 e, en segundo lugar, non é moi popular e, polo tanto, a tentación de ler e atopar unha solución en Internet reside na falta destas mesmas solucións.

O chip tamén ten folla de datos и manual de referencia RM0016, no primeiro hai enderezos de pinout e rexistro, no segundo - todo o demais. STM8 está programado en C nun IDE terrible Desenvolvemento visual ST.

Reloxo e E/S

Por defecto, STM8 opera a unha frecuencia de 2 MHz, isto debe ser corrixido inmediatamente.

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Reloxo HSI (Interno de Alta Velocidade).
O sinal de reloxo HSI deriva dun oscilador RC interno de 16 MHz cun divisor programable (1 a 8). Establécese no rexistro divisor do reloxo (CLK_CKDIVR).
Nota: ao comezo, selecciónase un oscilador HSI RC cun divisor de 8 como fonte principal do sinal de reloxo.

Atopamos o enderezo do rexistro na folla de datos, a descrición en refman e vemos que hai que borrar o rexistro:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Xa que imos executar PWM e conectar os LED, vexamos o pinout:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

O chip é pequeno, moitas funcións están suspendidas nos mesmos pinos. O que está entre corchetes é "funcionalidade alternativa", cámbiase por "bytes de opción" (bytes de opción) - algo así como fusibles Atmega. Podes cambiar os seus valores por programación, pero non é necesario, porque A nova funcionalidade só se activa despois dun reinicio. É máis fácil usar ST Visual Programmer (descargado con Visual Develop), que pode cambiar estes bytes. O pinout mostra que os pinos CH1 e CH2 do primeiro temporizador están agochados entre corchetes; é necesario configurar os bits AFR1 e AFR0 en STVP, e o segundo tamén transferirá a saída CH1 do segundo temporizador de PD4 a PC5.

Así, 6 pinos controlarán os LED: PC6, PC7 e PC3 para o primeiro temporizador, PC5, PD3 e PA3 para o segundo.

Configurar os propios pinos de E/S en STM8 é máis sinxelo e lóxico que en STM32:

  • familiar do rexistro de dirección de datos Atmega DDR (Rexistro de Dirección de Datos): 1 = saída;
  • o primeiro rexistro de control CR1, cando saia, establece o modo push-pull (1) ou dreno aberto (0); xa que conecto os leds ao chip con cátodos, aquí deixo ceros;
  • o segundo rexistro de control CR2, cando sae, establece a velocidade do reloxo: 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ón PWM

En primeiro lugar, imos definir os termos:

  • Frecuencia PWM - a frecuencia coa que corre o temporizador;
  • Recarga automática, AR – valor autocargable ata o cal contará o temporizador (período de pulso);
  • Evento de actualización, UEV – un evento que ocorre cando o temporizador contou para AR;
  • Ciclo de traballo PWM – Ciclo de traballo PWM, a miúdo chamado "factor de traballo";
  • Captura/Compara valor – valor para captura/comparación, ao que contou o temporizador fará algo (no caso de PWM, inverte o sinal de saída);
  • Valor de precarga - valor precargado. Comparar valor non pode cambiar mentres o temporizador está a funcionar, se non, o ciclo PWM romperase. Polo tanto, os novos valores transmitidos colócanse nun búfer e quítanse cando o temporizador chega ao final da súa conta atrás e se restablece;
  • Aliñado ao bordo и Modos aliñados ao centro – aliñación ao longo da fronteira e no centro, igual que a de Atmel PWM rápido и PWM de corrección de fase.
  • OCiREF, sinal de referencia de comparación de saída – sinal de saída de referencia, de feito, o que aparece no pin correspondente en modo PWM.

Como xa se desprende do pinout, dous temporizadores teñen capacidades PWM: o primeiro e o segundo. Ambos son de 16 bits, o primeiro ten moitas funcións adicionais (en particular, pode contar tanto para arriba como para abaixo). Necesitamos que os dous funcionen por igual, así que decidín comezar polo segundo obviamente máis pobre, para non usar accidentalmente algo que non está. Algún problema é que a descrición da funcionalidade PWM de todos os temporizadores no manual de referencia está no capítulo sobre o primeiro temporizador (modo 17.5.7 PWM), polo que tes que ir cara atrás e cara atrás ao longo do documento todo o tempo.

PWM en STM8 ten unha vantaxe importante sobre PWM en Atmega:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
PWM aliñado con límites
Configuración da conta de abaixo cara arriba
O reconto ascendente está activo se se borra o bit DIR do rexistro TIM_CR1
Exemplo
O exemplo usa o primeiro modo PWM. O sinal de referencia PWM OCiREF mantense alto mentres TIM1_CNT < TIM1_CCRi. Se non, fai falta un nivel baixo. Se o valor de comparación no rexistro TIM1_CCRi é maior que o valor de carga automática (rexistro TIM1_ARR), o sinal OCiREF mantense en 1. Se o valor de comparación é 0, OCiREF mantense en cero....

Temporizador STM8 durante evento de actualización comproba primeiro comparar valor, e só entón produce un sinal de referencia. O temporizador de Atmega primeiro se apaga e despois compara, o que resulta compare value == 0 a saída é unha agulla, que debe tratarse dalgún xeito (por exemplo, invertendo a lóxica mediante programación).

Entón o que queremos facer: PWM de 8 bits (AR == 255), contando de abaixo cara arriba, aliñación ao longo do bordo. Dado que as lámpadas están conectadas ao chip mediante cátodos, o PWM debería emitir 0 (LED acendido) ata que comparar valor e 1 despois.

Xa lemos sobre algúns Modo PWM, polo que atopamos o rexistro necesario do segundo temporizador buscando no manual de referencia esta frase (18.6.8 - TIMx_CCMR1):

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
110: Primeiro modo PWM: cando se conta de abaixo cara arriba, a primeira canle está activa mentres TIMx_CNT < TIMx_CCR1. En caso contrario, a primeira canle está inactiva. [máis no documento hai un copiar-pegar erróneo do temporizador 1] 111: Segundo modo PWM: ao contar de abaixo a arriba, a primeira canle está inactiva mentres TIMx_CNT < TIMx_CCR1. En caso contrario, a primeira canle está activa.

Dado que os LED están conectados ao MK mediante cátodos, o segundo modo convénnos (o primeiro tamén, pero aínda non o sabemos).

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Bit 3 OC1PE: Activar a precarga do pin 1
0: o rexistro de precarga en TIMx_CCR1 está desactivado. Podes escribir a TIMx_CCR1 en calquera momento. O novo valor funciona inmediatamente.
1: O rexistro de precarga en TIMx_CCR1 está activado. As operacións de lectura/escritura acceden ao rexistro de precarga. O valor precargado TIMx_CCR1 cárgase no rexistro de sombra durante cada evento de actualización.
*Nota: para que o modo PWM funcione correctamente, os rexistros de precarga deben estar activados. Isto non é necesario no modo de sinal único (o bit OPM está establecido no rexistro TIMx_CR1).

Está ben, imos activar todo o que necesitamos para as tres canles do segundo temporizador:

#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 dous rexistros de oito bits, todo é sinxelo:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

O segundo temporizador só pode contar de abaixo cara arriba, aliñación ao longo do bordo, non hai que cambiar nada. Axustemos o divisor de frecuencia, por exemplo, a 256. Para o segundo temporizador, o divisor está establecido no rexistro TIM2_PSCR e ten unha potencia de dous:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Só queda activar as conclusións e o propio temporizador. O primeiro problema resólvese mediante rexistros Capturar/Comparar Permitir: hai dous, tres canles espallados por eles de forma asimétrica. Aquí tamén podemos aprender que é posible cambiar a polaridade do sinal, é dicir. en principio, era posible usar o modo PWM 1. Escribimos:

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

E finalmente, iniciamos o temporizador no rexistro TIMx_CR1:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Escribamos un análogo sinxelo de AnalogWrite(), que transferirá os valores reais ao temporizador para a súa comparación. Os rexistros son nomeados previsiblemente Capturar/Comparar rexistros, hai dous deles para cada canle: os 8 bits de orde baixa en TIM2_CCRxL e os de orde superior en TIM2_CCRxH. Xa que creamos un PWM de 8 bits, é suficiente escribir só os bits menos significativos:

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

O lector atento notará que temos un PWM lixeiramente defectuoso, incapaz de producir o recheo do 100% (cun ​​valor máximo de 255, o sinal invírtese durante un ciclo de temporizador). Para os LED isto non importa, e o lector atento xa pode adiviñar como solucionalo.

PWM no segundo temporizador funciona, pasemos ao primeiro.

O primeiro temporizador ten exactamente os mesmos bits nos mesmos rexistros (só que eses bits que quedaron "reservados" no segundo temporizador úsanse activamente no primeiro para todo tipo de cousas avanzadas). Polo tanto, abonda con atopar os enderezos dos mesmos rexistros na folla de datos e copiar o código. Ben, cambia o valor do divisor de frecuencia, porque... o primeiro temporizador quere recibir non unha potencia de dous, senón un valor exacto de 16 bits en dous rexistros Preescalador alto и Baixo. Facemos de todo e... o primeiro temporizador non funciona. Que pasa?

O problema só se pode resolver mirando toda a sección sobre os rexistros de control do temporizador 1, onde buscamos o que non ten o segundo temporizador. Haberá 17.7.30 Pausa rexistro (TIM1_BKR), onde hai este bit:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Activar a saída principal

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Iso é todo seguro agora, o código no mesmo lugar.

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

Multiplex STM8

Multiplexación en STM8

O terceiro miniproxecto consiste en conectar oito LED RGB ao segundo temporizador en modo PWM e facelos mostrar cores diferentes. Baséase no concepto de multiplexación de LED, que é que se acendes e apagas os LED moi, moi rápido, pareceranos que están acesos constantemente (persistencia da visión, inercia da percepción visual). Unha vez fixen algo así en Arduino.

O algoritmo de traballo ten o seguinte aspecto:

  • conectou o ánodo do primeiro LED RGB;
  • acéndeo, enviando os sinais necesarios aos cátodos;
  • esperou ata o final do ciclo PWM;
  • conectado o ánodo do segundo LED RGB;
  • acendeu...

Pois, etc. Por suposto, para un bo funcionamento é necesario que o ánodo estea conectado e que o LED estea "encendido" ao mesmo tempo. Ben, ou case. En calquera caso, necesitamos escribir un código que emita valores en tres canles do segundo temporizador, cambialos cando se alcanza a UEV e, ao mesmo tempo, cambiar o LED RGB activo actualmente.

Dado que a conmutación de LED é automática, necesitamos crear unha "memoria de vídeo" desde a que o controlador de interrupcións recibirá datos. Esta é unha matriz sinxela:

uint8_t colors[8][3];

Para cambiar a cor dun LED específico, será suficiente escribir os valores necesarios nesta matriz. E a variable será a responsable do número do LED activo

uint8_t cnt;

Demux

Para unha multiplexación adecuada, necesitamos, curiosamente, un demultiplexor CD74HC238. Demultiplexer - un chip que implementa o operador no hardware <<. A través de tres pinos de entrada (bits 0, 1 e 2) alimámoslle un número X de tres bits, e en resposta activa o número de saída (1<<X). As entradas restantes do chip utilízanse para escalar todo o deseño. Necesitamos este chip non só para reducir o número de pinos ocupados do microcontrolador, senón tamén por seguridade, para non acender accidentalmente máis LED do posible e non queimar o MK. O chip custa un centavo e debe gardarse sempre no botiquín da casa.

O noso CD74HC238 encargarase de subministrar tensión ao ánodo do LED desexado. Nun múltiplex completo, proporcionaría tensión á columna a través dun P-MOSFET, pero nesta demostración é posible directamente, porque absorbe 20 mA, segundo cualificacións máximas absolutas na folla de datos. Desde Folla de datos CD74HC238 necesitamos pinouts e esta folla de trucos:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
H = nivel de alta tensión, L = nivel de baixa tensión, X - non me importa

Conectamos E2 e E1 a terra, E3, A0, A1 e A3 aos pines PD5, PC3, PC4 e PC5 de STM8. Dado que a táboa anterior contén niveis baixos e altos, configuramos estes pinos como pinos push-pull.

PWM

PWM no segundo temporizador está configurado do mesmo xeito que na historia anterior, con dúas diferenzas:

En primeiro lugar, necesitamos activar a interrupción Evento de actualización (UEV) que chamará a unha función que alterna o LED activo. Isto faise cambiando o bit Activar interrupción de actualización nun rexistro cun nome revelador

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
Interrupción de habilitación do rexistro

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

A segunda diferenza está relacionada co fenómeno da multiplexación, como fantasmas – brillo parasitario dos díodos. No noso caso, pode parecer debido ao feito de que o temporizador, que provocou unha interrupción no UEV, segue a funcionar e o controlador de interrupcións non ten tempo para cambiar o LED antes de que o temporizador comece a escribir algo nos pinos. Para combater isto, terás que inverter a lóxica (0 = brillo máximo, 255 = nada está iluminado) e evitar valores de ciclo de traballo extremos. Eses. asegúrese de que despois da UEV os LED se apaguen completamente durante un ciclo PWM.

Cambio de polaridade:

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

Evite configurar r, g e b en 255 e recordade invertelos cando os utilice.

Interrupcións

A esencia dunha interrupción é que en determinadas circunstancias o chip deixa de executar o programa principal e chama a algunha función externa. As interrupcións ocorren debido a influencias externas ou internas, incluído o temporizador.

Cando creamos por primeira vez un proxecto en ST Visual Develop, ademais de main.c recibimos unha fiestra cun arquivo misterioso stm8_interrupt_vector.c, incluído automaticamente no proxecto. Neste ficheiro asígnaselle unha función a cada interrupción NonHandledInterrupt. Necesitamos vincular a nosa función á interrupción desexada.

A folla de datos ten unha táboa de vectores de interrupción, onde atopamos os que necesitamos:

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8
13 Actualización/desbordamento de TIM2
14 Captura/comparación de TIM2

Necesitamos cambiar o LED na UEV, polo que necesitamos a interrupción #13.

En consecuencia, en primeiro lugar, no expediente stm8_interrupt_vector.c cambia o nome predeterminado da función responsable da interrupción número 13 (IRQ13) polo teu:

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

En segundo lugar, teremos que crear un ficheiro main.h co seguinte contido:

#ifndef __MAIN_H
#define __MAIN_H

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

E, finalmente, escribe esta función no teu 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;
}

Todo o que queda é habilitar as interrupcións. Isto faise usando o comando ensamblador rim - terás que buscalo Manual de programación:

//enable interrupts
_asm("rim");

Outro comando do ensamblador é sim - desactiva as interrupcións. Deben desactivarse mentres se escriben novos valores na "memoria de vídeo", para que unha interrupción causada no momento incorrecto non estrague a matriz.

Todo o código - en GitHub.

Ler as follas de datos 2: SPI en STM32; PWM, temporizadores e interrupcións en STM8

Se polo menos alguén considera útil este artigo, non o escribín en balde. Estarei encantado de recibir comentarios e observacións, tentarei responder todo.

Fonte: www.habr.com

Engadir un comentario