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:
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:
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.
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:
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:
... 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:
Ficha de Datos para microcontroladores STM32F103x8 e STM32F103xB;
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:
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:
"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í:
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):
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:
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):
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:
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:
Abre a sección 25.3.3 co título autoexplicativo "Configuración de SPI no modo mestre":
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):
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).
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):
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:
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:
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":
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":
<...>
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":
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:
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:
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:
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:
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.
(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":
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:
E, en consecuencia, conteñen bits que inclúen a sincronización de SPI2, IOPB (I/O Port B) e funcións alternativas (AFIO).
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.
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.
Por defecto, STM8 opera a unha frecuencia de 2 MHz, isto debe ser corrixido inmediatamente.
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:
Xa que imos executar PWM e conectar os LED, vexamos o pinout:
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
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:
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):
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).
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:
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:
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:
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:
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:
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:
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
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.
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:
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:
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.
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.