Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
В a primeira parte Tentei dizer aos engenheiros eletrônicos amadores que cresceram com as calças Arduino como e por que deveriam ler folhas de dados e outras documentações para microcontroladores. O texto acabou sendo grande, então prometi mostrar exemplos práticos em um artigo separado. Bem, ele se autodenominava cogumelo do leite...
Hoje vou mostrar como usar planilhas de dados para resolver tarefas bastante simples, mas necessárias para muitos projetos, nos controladores STM32 (Blue Pill) e STM8. Todos os projetos de demonstração são dedicados aos meus LEDs favoritos, iremos acendê-los em grandes quantidades, para os quais teremos que utilizar todo tipo de periféricos interessantes.
O texto novamente ficou enorme, então por conveniência estou fazendo o conteúdo:
Disclaimer: Não sou engenheiro, não pretendo ter profundo conhecimento em eletrônica, o artigo é destinado a amadores como eu. Na verdade, há dois anos eu me considerava o público-alvo. Se alguém tivesse me dito que as folhas de dados em um chip desconhecido não eram assustadoras de ler, eu não teria passado muito tempo procurando alguns trechos de código na Internet e inventando muletas com tesoura e fita adesiva.
O foco deste artigo está nas planilhas de dados, não nos projetos; portanto, o código pode não ser muito organizado e muitas vezes limitado. Os projetos em si são muito simples, embora adequados para um primeiro contato com o novo chip.
Espero que meu artigo ajude alguém em um estágio semelhante de imersão no hobby.
STM32
16 LEDs com DM634 e SPI
Um pequeno projeto usando Blue Pill (STM32F103C8T6) e driver DM634 LED. Usando planilhas de dados, descobriremos o driver, as portas STM IO e configuraremos o SPI.
DM634
Chip taiwanês com 16 saídas PWM de 16 bits, pode ser conectado em cadeias. O modelo low-end de 12 bits é conhecido de um projeto doméstico Pacote de luz. Ao mesmo tempo, escolhendo entre o DM63x e o conhecido TLC5940, escolhi o DM por vários motivos: 1) O TLC no Aliexpress é definitivamente falso, mas este não é; 2) DM possui PWM autônomo com gerador de frequência próprio; 3) poderia ser comprado barato em Moscou, em vez de esperar por um pacote de Ali. E, claro, foi interessante aprender como controlar o chip sozinho, em vez de usar uma biblioteca pronta. Os chips agora são apresentados principalmente no pacote SSOP24 e são fáceis de soldar em um adaptador.
Como o fabricante é taiwanês, Ficha de dados o chip está escrito em chinês e inglês, o que significa que será divertido. Primeiro olhamos para a pinagem (Conexão de pino) para entender a qual perna conectar o que e uma descrição dos pinos (Descrição do pino). 16 pinos:
Fontes de dissipador DC (dreno aberto)
Pia / Saída de drenagem aberta - ralo; fonte de corrente de entrada; a saída é conectada ao terra no estado ativo - os LEDs são conectados ao driver por cátodos. Eletricamente, isso não é, obviamente, um “dreno aberto” (dreno aberto), mas nas folhas de dados esta designação para pinos no modo de drenagem é frequentemente encontrada.
Resistores externos entre REXT e GND para definir o valor da corrente de saída
Entre o pino REXT e o terra é instalado um resistor de referência que controla a resistência interna das saídas, veja gráfico na página 9 da ficha técnica. No DM634, esta resistência também pode ser controlada por software, configurando o brilho geral (brilho global); Não entrarei em detalhes neste artigo, apenas colocarei um resistor de 2.2 - 3 kOhm aqui.
Para entender como controlar o chip, vejamos a descrição da interface do dispositivo:
Sim, aqui está, chinês inglês em toda a sua glória. Traduzir isso é problemático, você pode entender se quiser, mas há outra maneira - veja como a conexão com o TLC5940 funcionalmente semelhante é descrita na folha de dados:
... Apenas três pinos são necessários para inserir dados no dispositivo. A borda ascendente do sinal SCLK desloca os dados do pino SIN para o registro interno. Depois que todos os dados foram carregados, um sinal XLAT curto e alto trava os dados transferidos sequencialmente nos registros internos. Registradores internos são portas acionadas pelo nível do sinal XLAT. Todos os dados são transmitidos primeiro com o bit mais significativo.
Trava – trava/trava/trava. Borda ascendente – borda principal do pulso MSB primeiro – bit mais significativo (mais à esquerda) à frente. para dados de relógio – transmitir dados sequencialmente (bit a bit).
Palavra trinco é frequentemente encontrado na documentação de chips e é traduzido de várias maneiras, portanto, para fins de compreensão, me permitirei
pequeno programa educacionalO driver de LED é essencialmente um registrador de deslocamento. "Mudança" (mudança) no nome - movimento bit a bit de dados dentro do dispositivo: cada novo bit inserido dentro dele empurra toda a cadeia para frente. Como ninguém quer observar o piscar caótico dos LEDs durante o turno, o processo ocorre em registros buffer separados dos registros de trabalho por um amortecedor (trinco) é uma espécie de sala de espera onde os bits são dispostos na sequência desejada. Quando tudo estiver pronto, a veneziana se abre e as brocas entram em ação, substituindo o lote anterior. Palavra trinco na documentação de microcircuitos quase sempre implica tal amortecedor, não importa em quais combinações ele seja usado.
Assim, a transferência de dados para o DM634 é feita da seguinte forma: configure a entrada DAI para o valor do bit mais significativo do LED distante, puxe o DCK para cima e para baixo; defina a entrada DAI para o valor do próximo bit, puxe DCK; e assim por diante até que todos os bits tenham sido transmitidos (programado), após o qual extraímos LAT. Isso pode ser feito manualmente (estrondo), mas é melhor utilizar uma interface SPI especialmente adaptada para isso, pois ela é apresentada em nosso STM32 em duas vias.
Comprimido Azul STM32F103
Introdução: Os controladores STM32 são muito mais complexos que o Atmega328 do que podem parecer assustadores. Além disso, por razões de economia de energia, quase todos os periféricos estão desligados na inicialização e a frequência do clock é de 8 MHz da fonte interna. Felizmente, os programadores STM escreveram um código que eleva o chip aos 72 MHz “calculados”, e os autores de todos os IDEs que conheço o incluíram no procedimento de inicialização, então não precisamos de clock (mas você pode se realmente quiser). Mas você terá que ligar os periféricos.
Documentação: Blue Pill está equipado com o popular chip STM32F103C8T6, existem dois documentos úteis para isso:
Ficha de Dados para microcontroladores STM32F103x8 e STM32F103xB;
Pinouts – pinouts de chip – caso decidamos fazer nós mesmos as placas;
Mapa de memória – mapa de memória para um chip específico. O Manual de Referência possui um mapa de toda a linha e menciona registros que o nosso não possui.
Tabela de Definições de Pinos – listando as funções principais e alternativas dos pinos; para a “pílula azul” você pode encontrar fotos mais convenientes na Internet com uma lista de pinos e suas funções. Portanto, imediatamente pesquisamos a pinagem do Blue Pill no Google e mantemos esta imagem em mãos:
Obs: houve um erro na imagem da Internet, que foi anotado nos comentários, obrigado por isso. A imagem foi substituída, mas esta é uma lição - é melhor verificar as informações não nas fichas técnicas.
Retiramos a ficha técnica, abrimos o Manual de Referência e a partir de agora utilizamos apenas ele.
Procedimento: tratamos de entrada/saída padrão, configuramos SPI, ligamos os periféricos necessários.
Entrada Saída
No Atmega328, a E/S é implementada de forma extremamente simples, e é por isso que a abundância de opções do STM32 pode ser confusa. Agora só precisamos de conclusões, mas mesmo estas têm quatro opções:
"Puxe empurre" (empurrar puxar) é a saída normal do Arduino, o pino pode assumir o valor HIGH ou LOW. Mas com o “dreno aberto” existem complexidade, embora na verdade tudo seja simples aqui:
Configuração de saída / quando a porta é atribuída à saída: / buffer de saída habilitado: / – modo dreno aberto: “0” no registro de saída habilita N-MOS, “1” no registro de saída deixa a porta no modo Hi-Z ( P-MOS não está ativado) / – modo push-pull: “0” no registro de saída ativa N-MOS, “1” no registro de saída ativa P-MOS.
Toda a diferença entre dreno aberto (dreno aberto) de “empurrar-puxar” (empurrar puxar) é que o primeiro pino não pode aceitar o estado HIGH: ao escrever um no registro de saída, ele entra no modo de alta resistência (impedância elevada, Olá-Z). Ao escrever zero, o pino se comporta da mesma forma em ambos os modos, tanto logicamente quanto eletricamente.
No modo de saída normal, o pino simplesmente transmite o conteúdo do registrador de saída. Na “alternativa” é controlado pelos periféricos correspondentes (ver 9.1.4):
Se um bit de porta estiver configurado como um pino de função alternativa, o registro do pino será desabilitado e o pino será conectado ao pino periférico.
A funcionalidade alternativa de cada pino é descrita em Pin Definições A folha de dados está na imagem baixada. À pergunta sobre o que fazer se um pino tiver diversas funções alternativas, a resposta é dada por uma nota de rodapé na ficha técnica:
Se vários periféricos usarem o mesmo pino, para evitar conflito entre funções alternativas, apenas um periférico deverá ser usado por vez, alternado usando o bit de habilitação do relógio periférico (no registro RCC apropriado).
Finalmente, os pinos no modo de saída também possuem uma velocidade de clock. Este é outro recurso de economia de energia; no nosso caso, apenas colocamos no máximo e esquecemos.
Então: estamos usando SPI, o que significa que dois pinos (com dados e com sinal de clock) devem ser “função push-pull alternativa”, e outro (LAT) deve ser “push-pull regular”. Mas antes de atribuí-los, vamos tratar do SPI.
SPI
Outro pequeno programa educacional
SPI ou Serial Peripheral Interface (interface periférica serial) é uma interface simples e muito eficaz para conectar um MK com outros MKs e com o mundo exterior em geral. O princípio de seu funcionamento já foi descrito acima, no que se refere ao driver de LED chinês (no manual de referência, ver seção 25). O SPI pode operar nos modos mestre (“master”) e escravo (“escravo”). O SPI possui quatro canais básicos, dos quais nem todos podem ser utilizados:
MOSI, Master Output / Slave Input: este pino transmite dados no modo mestre e recebe dados no modo escravo;
MISO, Master Input / Slave Output: ao contrário, recebe no mestre e transmite no escravo;
SCK, Serial Clock: ajusta a frequência de transmissão de dados no mestre ou recebe sinal de clock no escravo. Essencialmente batendo batidas;
SS, Slave Select: com a ajuda deste canal, o escravo sabe que algo é desejado dele. No STM32 é denominado NSS, onde N = negativo, ou seja, o controlador se torna escravo se houver aterramento neste canal. Combina bem com o modo Open Drain Output, mas isso é outra história.
Como tudo mais, o SPI no STM32 é rico em funcionalidades, o que o torna um tanto difícil de entender. Por exemplo, pode funcionar não só com SPI, mas também com interface I2S, e na documentação suas descrições estão misturadas, é necessário cortar o excesso em tempo hábil. Nossa tarefa é extremamente simples: basta enviar os dados utilizando apenas MOSI e SCK. Vamos para a seção 25.3.4 (comunicação half-duplex, comunicação half-duplex), onde encontramos 1 relógio e 1 fio de dados unidirecional (1 sinal de clock e 1 fluxo de dados unidirecional):
Neste modo, o aplicativo usa SPI no modo somente transmissão ou somente recebimento. / O modo somente transmissão é semelhante ao modo duplex: os dados são transmitidos no pino de transmissão (MOSI no modo mestre ou MISO no modo escravo) e o pino de recepção (MISO ou MOSI respectivamente) pode ser usado como um pino de E/S normal . Neste caso, a aplicação só precisa ignorar o buffer Rx (se for lido, não haverá dados transferidos para lá).
Ótimo, o pino MISO está livre, vamos conectar o sinal LAT a ele. Vejamos o Slave Select, que no STM32 pode ser controlado programaticamente, o que é extremamente conveniente. Lemos o parágrafo de mesmo nome na seção 25.3.1 Descrição Geral do SPI:
Controle de software NSS (SSM = 1) / As informações de seleção de escravo estão contidas no bit SSI do registro SPI_CR1. O pino NSS externo permanece livre para outras necessidades de aplicação.
É hora de escrever nos registros. Resolvi usar o SPI2, procure seu endereço base no datasheet - na seção 3.3 Mapa de Memória:
Abra a seção 25.3.3 com o título autoexplicativo “Configurando SPI no modo Master”:
1. Defina a frequência do clock serial com os bits BR[2:0] no registro SPI_CR1.
Os registros são coletados na seção do manual de referência de mesmo nome. Mudança de endereço (Deslocamento de endereço) para CR1 – 0x00, por padrão todos os bits são limpos (Redefinir valor 0x0000):
Os bits BR definem o divisor de clock do controlador, determinando assim a frequência na qual o SPI irá operar. Nossa frequência STM32 será de 72 MHz, o driver de LED, conforme sua ficha técnica, opera com frequência de até 25 MHz, então precisamos dividir por quatro (BR[2:0] = 001).
2. Defina os bits CPOL e CPHA para definir a relação entre a transferência de dados e a temporização do relógio serial (veja o diagrama na página 240)
Como estamos lendo uma folha de dados aqui e não olhando esquemas, vamos dar uma olhada mais de perto na descrição de texto dos bits CPOL e CPHA na página 704 (Descrição Geral do SPI):
Fase e polaridade do relógio
Usando os bits CPOL e CPHA do registro SPI_CR1, você pode selecionar programaticamente quatro relacionamentos de temporização. O bit CPOL (polaridade do clock) controla o estado do sinal do clock quando nenhum dado está sendo transmitido. Este bit controla os modos mestre e escravo. Se o CPOL for redefinido, o pino SCK estará baixo no modo de repouso. Se o bit CPOL estiver definido, o pino SCK estará alto durante o modo de repouso.
Quando o bit CPHA (fase do relógio) é definido, o estroboscópio de bit alto é a segunda borda do sinal SCK (caindo se o CPOL estiver limpo, aumentando se o CPOL estiver definido). Os dados são capturados pela segunda mudança no sinal do relógio. Se o bit CPHA estiver limpo, o estroboscópio de bit alto será a borda ascendente do sinal SCK (borda descendente se CPOL estiver definido, borda ascendente se CPOL estiver limpo). Os dados são capturados na primeira mudança no sinal do relógio.
Tendo absorvido esse conhecimento, chegamos à conclusão que ambos os bits devem permanecer zeros, pois Queremos que o sinal SCK permaneça baixo quando não estiver em uso e que os dados sejam transmitidos na borda ascendente do pulso (veja a Fig. Borda Ascendente na ficha técnica do DM634).
A propósito, aqui encontramos pela primeira vez uma característica do vocabulário nas planilhas de dados ST: nelas está escrita a frase “redefinir o bit para zero” para redefinir um poucoE não para limpar um pouco, como, por exemplo, Atmega.
3. Defina o bit DFF para determinar se o bloco de dados tem formato de 8 ou 16 bits
Peguei especificamente um DM16 de 634 bits para não me preocupar em transmitir dados PWM de 12 bits, como o DM633. Faz sentido definir DFF como um:
4. Configure o bit LSBFIRST no registro SPI_CR1 para determinar o formato do bloco
LSBFIRST, como o próprio nome sugere, configura a transmissão com o bit menos significativo primeiro. Mas o DM634 quer receber dados começando pelo bit mais significativo. Portanto, deixamos ele redefinido.
5. No modo de hardware, se for necessária a entrada do pino NSS, aplique um sinal alto ao pino NSS durante toda a sequência de transferência de bytes. No modo de software NSS, defina os bits SSM e SSI no registro SPI_CR1. Se o pino NSS for usado como saída, apenas o bit SSOE precisará ser definido.
Instale 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. Os bits MSTR e SPE devem ser setados (eles permanecem setados apenas se o sinal NSS estiver alto)
Na verdade, com esses bits designamos nosso SPI como mestre e o ligamos:
O SPI está configurado, vamos escrever imediatamente funções que enviam bytes para o driver. Continue lendo 25.3.3 “Configurando SPI no modo mestre”:
Ordem de transferência de dados
A transmissão começa quando um byte é gravado no buffer Tx.
O byte de dados é carregado no registrador de deslocamento em paralelo modo (do barramento interno) durante a transmissão do primeiro bit, após o qual ele é transmitido para sequencial Modo de pino MOSI, primeiro ou último bit adiante dependendo da configuração do bit LSBFIRST no registro CPI_CR1. O sinalizador TXE é definido após a transmissão de dados do buffer Tx para o registrador de deslocamento, e também gera uma interrupção se o bit TXEIE no registro CPI_CR1 estiver definido.
Destaquei algumas palavras na tradução para chamar a atenção para uma característica da implementação do SPI em controladores STM. No Atmega a bandeira TXE (Tx vazio, Tx está vazio e pronto para receber dados) é definido somente após todo o byte ter sido enviado fora. E aqui este sinalizador é definido após o byte ter sido inserido no registrador de deslocamento interno. Como ele é empurrado para lá com todos os bits ao mesmo tempo (em paralelo) e depois os dados são transferidos sequencialmente, o TXE é definido antes que o byte seja completamente enviado. Isto é importante porque no caso do nosso driver de LED, precisamos puxar o pino LAT após enviar todos dados, ou seja, A bandeira TXE por si só não será suficiente para nós.
Isso significa que precisamos de outra bandeira. Vejamos 25.3.7 - “Sinalizadores de status”:
<…>
Sinalizador OCUPADO
O sinalizador BSY é definido e limpo pelo hardware (gravar nele não tem efeito). O sinalizador BSY indica o estado da camada de comunicação SPI.
Ele redefine:
quando a transferência for concluída (exceto no modo mestre se a transferência for contínua)
quando o SPI está desabilitado
quando ocorre um erro no modo mestre (MODF=1)
Se a transferência não for contínua, o sinalizador BSY é apagado entre cada transferência de dados
Ok, isso será útil. Vamos descobrir onde o buffer Tx está localizado. Para fazer isso, leia “Registro de Dados SPI”:
Bits 15:0 DR[15:0] Registro de dados
Dados recebidos ou dados a serem transmitidos.
O registrador de dados é dividido em dois buffers - um para escrita (buffer de transmissão) e outro para leitura (buffer de recepção). Escrever no registrador de dados grava no buffer Tx e ler no registrador de dados retornará o valor contido no buffer Rx.
Bem, e o registrador de status, onde se encontram os flags TXE e BSY:
Bom, como precisamos transmitir 16 vezes dois bytes, de acordo com o número de saídas do driver de LED, algo assim:
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();
}
Mas ainda não sabemos como puxar o pino LAT, então voltaremos ao I/O.
Atribuindo pinos
No STM32F1, os registros responsáveis pelo estado dos pinos são bastante incomuns. É claro que existem mais deles do que Atmega, mas também são diferentes de outros chips STM. Seção 9.1 Descrição Geral do GPIO:
Cada uma das portas de E/S de uso geral (GPIO) tem dois registradores de configuração de 32 bits (GPIOx_CRL e GPIOx_CRH), dois registradores de dados de 32 bits (GPIOx_IDR e GPIOx_ODR), um registrador de set/reset de 32 bits (GPIOx_BSRR), um registrador de reset de 16 bits (GPIOx_BRR) e um registrador de 32 bits. registro de bloqueio de bits (GPIOx_LCKR).
Os dois primeiros registros são incomuns e também bastante inconvenientes, porque os 16 pinos das portas estão espalhados entre eles em um formato de “quatro bits por irmão”. Aqueles. os pinos de zero a sete estão em CRL e o restante está em CRH. Ao mesmo tempo, os registros restantes contêm com sucesso os bits de todos os pinos da porta - muitas vezes permanecendo metade “reservados”.
Para simplificar, vamos começar do final da lista.
Não precisamos de um registro de bloqueio.
Os registros set e reset são bastante engraçados porque se duplicam parcialmente: você pode escrever tudo apenas em BSRR, onde os 16 bits mais altos irão redefinir o pino para zero, e os mais baixos serão definidos para 1, ou você também pode use BRR, cujos 16 bits inferiores apenas redefinem o pino. Eu gosto da segunda opção. Esses registros são importantes porque fornecem acesso atômico aos pinos:
Conjunto ou reinicialização atômica
Não há necessidade de desabilitar interrupções ao programar GPIOx_ODR no nível de bit: um ou mais bits podem ser alterados com uma única operação de gravação atômica APB2. Isso é conseguido escrevendo um "1" no registro de set/reset (GPIOx_BSRR ou, apenas para reset, GPIOx_BRR) do bit que precisa ser alterado. Outros bits permanecerão inalterados.
Os registradores de dados possuem nomes bastante autoexplicativos - IDR = Entrada Registro de direção, registro de entrada; ODR = saída Registro de direção, registro de saída. Não precisaremos deles no projeto atual.
E finalmente, os registros de controle. Como estamos interessados nos segundos pinos SPI, nomeadamente PB13, PB14 e PB15, olhamos imediatamente para CRH:
E vemos que precisaremos escrever algo em bits de 20 a 31.
Já descobrimos acima o que queremos dos pinos, então aqui farei sem captura de tela, direi apenas que MODE especifica a direção (entrada se ambos os bits estiverem definidos como 0) e a velocidade do pino (precisamos de 50 MHz, ou seja, ambos os pinos para “1”), e CNF define o modo: regular “push-pull” – 00, “alternativo” – 10. Por padrão, como vemos acima, todos os pinos têm o terceiro bit a partir da parte inferior (CNF0), isso os coloca no modo entrada flutuante.
Como pretendo fazer outra coisa com este chip, para simplificar, defini todos os valores possíveis de MODE e CNF para os registros de controle inferior e superior.
(LAT_low só por inércia, sempre foi assim, deixe ficar)
Agora está tudo ótimo, mas não funciona. Por se tratar do STM32, eles economizam eletricidade, o que significa que você precisa habilitar o clock dos periféricos necessários.
Ative o relógio
O relógio, também conhecido como Clock, é responsável pela cronometragem. E já pudemos notar a abreviatura RCC. Procuramos na documentação: trata-se de Reset e Controle de Relógio.
Como foi dito acima, felizmente, a parte mais difícil do tópico de cronometragem foi feita para nós pelo pessoal da STM, pelo que agradecemos muito (mais uma vez darei um link para Site de Di Halt, para deixar claro o quão confuso é). Precisamos apenas de registradores responsáveis por habilitar o clock periférico (Peripheral Clock Enable Registers). Primeiro, vamos encontrar o endereço base do RCC, ele está bem no início do “Mapa de Memória”:
E então clique no link onde você tenta encontrar algo na placa, ou, melhor ainda, percorra as descrições dos registros habilitadores nas seções sobre habilitar registros. Onde encontraremos RCC_APB1ENR e RCC_APB2ENR:
E eles, portanto, contêm bits que incluem o clock de SPI2, IOPB (porta de E/S B) e funções alternativas (AFIO).
Se você tiver oportunidade e vontade de testar, então conecte o DM634 assim: DAI ao PB15, DCK ao PB13, LAT ao PB14. Alimentamos o driver com 5 volts, não esqueça de conectar os aterramentos.
STM8 PWM
PWM em STM8
Quando eu estava planejando este artigo, decidi, por exemplo, tentar dominar algumas funcionalidades de um chip desconhecido usando apenas uma ficha técnica, para não acabar com um sapateiro sem botas. O STM8 era ideal para esta função: em primeiro lugar, tinha algumas placas chinesas com STM8S103 e, em segundo lugar, não é muito popular e, portanto, a tentação de ler e encontrar uma solução na Internet reside na falta destas mesmas soluções.
Por padrão, o STM8 opera na frequência de 2 MHz, isso deve ser corrigido imediatamente.
Relógio HSI (interno de alta velocidade)
O sinal de clock HSI é derivado de um oscilador RC interno de 16 MHz com um divisor programável (1 a 8). É definido no registro divisor de clock (CLK_CKDIVR).
Nota: no início, um oscilador HSI RC com um divisor de 8 é selecionado como fonte principal do sinal de clock.
Encontramos o endereço do registro na folha de dados, a descrição em refman e vemos que o registro precisa ser limpo:
Como vamos executar o PWM e conectar os LEDs, vamos dar uma olhada na pinagem:
O chip é pequeno, muitas funções estão suspensas nos mesmos pinos. O que está entre colchetes é “funcionalidade alternativa”, é trocado por “bytes de opção” (bytes de opção) – algo como fusíveis Atmega. Você pode alterar seus valores programaticamente, mas não é necessário, pois A nova funcionalidade é ativada somente após uma reinicialização. É mais fácil usar o ST Visual Programmer (baixado com Visual Develop), que pode alterar esses bytes. A pinagem mostra que os pinos CH1 e CH2 do primeiro temporizador estão ocultos entre colchetes; é necessário configurar os bits AFR1 e AFR0 no STVP, e o segundo também transferirá a saída CH1 do segundo temporizador de PD4 para PC5.
Assim, 6 pinos controlarão os LEDs: PC6, PC7 e PC3 para o primeiro temporizador, PC5, PD3 e PA3 para o segundo.
Configurar os próprios pinos de E/S no STM8 é mais simples e lógico do que no STM32:
familiarizado com o registro de direção de dados Atmega DDR (Registro de direção de dados): 1 = saída;
o primeiro registro de controle CR1, quando emitido, define o modo push-pull (1) ou dreno aberto (0); como conecto os LEDs ao chip com cátodos, deixo zeros aqui;
o segundo registro de controle CR2, quando emitido, define a velocidade do clock: 1 = 10 MHz
Freqüência PWM – frequência com que o cronômetro funciona;
Recarga automática, AR – valor autocarregável até o qual o temporizador irá contar (período de pulso);
Evento de atualização, UEV – um evento que ocorre quando o cronômetro conta para AR;
Ciclo de trabalho PWM – Ciclo de trabalho PWM, muitas vezes chamado de “fator de serviço”;
Capturar/Comparar Valor – valor para captura/comparação, até o qual o temporizador contou fará alguma coisa (no caso de PWM inverte o sinal de saída);
Valor de pré-carregamento – valor pré-carregado. Comparar valor não pode mudar enquanto o temporizador estiver funcionando, caso contrário o ciclo PWM será interrompido. Portanto, novos valores transmitidos são colocados em um buffer e retirados quando o cronômetro chega ao final de sua contagem regressiva e é zerado;
Alinhado nas bordas и Modos alinhados ao centro – alinhamento ao longo da borda e centro, igual ao de Atmel PWM rápido и PWM com correção de fase.
OCiREF, sinal de referência de comparação de saída – sinal de saída de referência, na verdade, o que aparece no pino correspondente no modo PWM.
Como já está claro na pinagem, dois temporizadores possuem recursos PWM – o primeiro e o segundo. Ambos são de 16 bits, o primeiro possui muitos recursos adicionais (em particular, pode contar tanto para cima quanto para baixo). Precisamos que ambos funcionem igualmente, então decidi começar com o segundo, obviamente mais pobre, para não usar acidentalmente algo que não existe. Um problema é que a descrição da funcionalidade PWM de todos os temporizadores no manual de referência está no capítulo sobre o primeiro temporizador (17.5.7 Modo PWM), então você tem que ir e voltar ao longo do documento o tempo todo.
O PWM no STM8 tem uma vantagem importante sobre o PWM no Atmega:
PWM alinhado ao limite
Configuração da conta de baixo para cima
A contagem ascendente está ativa se o bit DIR no registro TIM_CR1 for limpo
Exemplo
O exemplo usa o primeiro modo PWM. O sinal de referência PWM OCiREF é mantido alto enquanto TIM1_CNT < TIM1_CCRi. Caso contrário, será necessário um nível baixo. Se o valor de comparação no registro TIM1_CCRi for maior que o valor de carregamento automático (registro TIM1_ARR), o sinal OCiREF será mantido em 1. Se o valor de comparação for 0, OCiREF será mantido em zero....
Temporizador STM8 durante evento de atualização verifica primeiro comparar valor, e só então produz um sinal de referência. O cronômetro do Atmega primeiro falha e depois compara, resultando em compare value == 0 a saída é uma agulha, que deve ser tratada de alguma forma (por exemplo, invertendo programaticamente a lógica).
Então, o que queremos fazer: PWM de 8 bits (AR == 255), contando de baixo para cima, alinhamento ao longo da borda. Como as lâmpadas estão conectadas ao chip por cátodos, o PWM deve emitir 0 (LED aceso) até comparar valor e 1 depois.
Já lemos sobre alguns Modo PWM, então encontramos o registro necessário do segundo temporizador pesquisando esta frase no manual de referência (18.6.8 - TIMx_CCMR1):
110: Primeiro modo PWM – ao contar de baixo para cima, o primeiro canal fica ativo enquanto TIMx_CNT < TIMx_CCR1. Caso contrário, o primeiro canal estará inativo. [mais adiante no documento há um copiar e colar errado do temporizador 1] 111: Segundo modo PWM – ao contar de baixo para cima, o primeiro canal fica inativo enquanto TIMx_CNT < TIMx_CCR1. Caso contrário, o primeiro canal estará ativo.
Como os LEDs estão conectados ao MK por cátodos, o segundo modo nos convém (o primeiro também, mas ainda não sabemos).
Bit 3 OC1PE: Habilita pré-carga do pino 1
0: O registro de pré-carga em TIMx_CCR1 está desabilitado. Você pode gravar em TIMx_CCR1 a qualquer momento. O novo valor funciona imediatamente.
1: O registro de pré-carga em TIMx_CCR1 está habilitado. As operações de leitura/gravação acessam o registro de pré-carga. O valor pré-carregado TIMx_CCR1 é carregado no registrador shadow durante cada evento de atualização.
*Nota: Para que o modo PWM funcione corretamente, os registros de pré-carga devem estar habilitados. Isto não é necessário no modo de sinal único (o bit OPM é definido no registro TIMx_CR1).
Ok, vamos ligar tudo o que precisamos para os três canais do segundo temporizador:
O segundo cronômetro só pode contar de baixo para cima, alinhamento ao longo da borda, nada precisa ser alterado. Vamos definir o divisor de frequência, por exemplo, para 256. Para o segundo temporizador, o divisor é definido no registro TIM2_PSCR e é uma potência de dois:
Resta ligar as conclusões e o próprio segundo cronômetro. O primeiro problema é resolvido por registradores Capturar/Comparar permitir: existem dois, três canais espalhados por eles de forma assimétrica. Aqui também podemos aprender que é possível alterar a polaridade do sinal, ou seja, em princípio, foi possível usar o Modo PWM 1. Escrevemos:
Vamos escrever um análogo simples de AnalogWrite(), que transferirá os valores reais para o temporizador para comparação. Os registros são nomeados de forma previsível Capturar/Comparar registros, existem dois deles para cada canal: os 8 bits de ordem inferior em TIM2_CCRxL e os de ordem superior em TIM2_CCRxH. Como criamos um PWM de 8 bits, basta escrever apenas os bits menos significativos:
O leitor atento notará que temos um PWM ligeiramente defeituoso, incapaz de produzir 100% de preenchimento (no valor máximo de 255, o sinal é invertido por um ciclo do temporizador). Para os LEDs isso não importa, e o leitor atento já pode adivinhar como consertar.
O PWM funciona no segundo temporizador, vamos passar para o primeiro.
O primeiro temporizador tem exatamente os mesmos bits nos mesmos registros (só que os bits que permaneceram “reservados” no segundo temporizador são usados ativamente no primeiro para todos os tipos de coisas avançadas). Portanto, basta encontrar os endereços dos mesmos registros na ficha técnica e copiar o código. Bem, mude o valor do divisor de frequência, porque... o primeiro temporizador deseja receber não uma potência de dois, mas um valor exato de 16 bits em dois registros Pré-escalador alto и Baixo. Fazemos tudo e... o primeiro temporizador não funciona. Qual é o problema?
O problema só pode ser resolvido examinando toda a seção sobre os registros de controle do temporizador 1, onde procuramos aquele que o segundo temporizador não possui. Haverá 17.7.30 Quebra de registro (TIM1_BKR), onde há este bit:
O terceiro miniprojeto consiste em conectar oito LEDs RGB ao segundo temporizador em modo PWM e fazê-los mostrar cores diferentes. É baseado no conceito de multiplexação de LEDs, que é que se você ligar e desligar os LEDs muito, muito rapidamente, nos parecerá que eles estão constantemente ligados (persistência de visão, inércia da percepção visual). uma vez eu fiz algo assim no Arduino.
O algoritmo de trabalho é assim:
conectou o ânodo do primeiro LED RGB;
acendeu-o, enviando os sinais necessários aos cátodos;
esperou até o final do ciclo PWM;
conectou o ânodo do segundo LED RGB;
acenda...
Bem, etc. Claro, para uma operação bonita é necessário que o ânodo esteja conectado e o LED esteja “aceso” ao mesmo tempo. Bem, ou quase. Em qualquer caso, precisamos escrever um código que irá gerar valores em três canais do segundo temporizador, alterá-los quando o UEV for atingido e, ao mesmo tempo, alterar o LED RGB atualmente ativo.
Como a comutação dos LEDs é automática, precisamos criar uma “memória de vídeo” da qual o manipulador de interrupções receberá os dados. Esta é uma matriz simples:
uint8_t colors[8][3];
Para alterar a cor de um determinado LED, bastará escrever os valores necessários neste array. E a variável será responsável pelo número do LED ativo
uint8_t cnt;
Demux
Para uma multiplexação adequada, precisamos, curiosamente, de um demultiplexador CD74HC238. Demultiplexador - um chip que implementa o operador em hardware <<. Através de três pinos de entrada (bits 0, 1 e 2) alimentamos um número X de três bits e, em resposta, ele ativa o número de saída (1<<X). As entradas restantes do chip são usadas para dimensionar todo o design. Precisamos deste chip não só para reduzir o número de pinos ocupados do microcontrolador, mas também para segurança - para não acender acidentalmente mais LEDs do que possível e não queimar o MK. O chip custa um centavo e deve ser sempre guardado no armário de remédios de sua casa.
Nosso CD74HC238 será responsável por fornecer tensão ao ânodo do LED desejado. Em um multiplex completo, ele forneceria tensão à coluna através de um P-MOSFET, mas nesta demonstração é possível diretamente, porque consome 20 mA, de acordo com classificações máximas absolutas na folha de dados. De Ficha técnica CD74HC238 precisamos de pinagens e desta folha de dicas:
H = nível de alta tensão, L = nível de baixa tensão, X – não importa
Conectamos E2 e E1 ao terra, E3, A0, A1 e A3 aos pinos PD5, PC3, PC4 e PC5 do STM8. Como a tabela acima contém níveis baixos e altos, configuramos esses pinos como pinos push-pull.
PWM
O PWM no segundo temporizador é configurado da mesma forma que na história anterior, com duas diferenças:
Primeiro, precisamos habilitar a interrupção em Evento de atualização (UEV) que chamará uma função que alterna o LED ativo. Isso é feito alterando o bit Ativar interrupção de atualização em um registro com um nome revelador
A segunda diferença está relacionada ao fenômeno da multiplexação, como ghosting – brilho parasita de diodos. No nosso caso, pode aparecer devido ao fato de o temporizador, tendo causado uma interrupção no UEV, continuar a funcionar, e o manipulador de interrupção não ter tempo de mudar o LED antes que o temporizador comece a escrever algo nos pinos. Para combater isso, você terá que inverter a lógica (0 = brilho máximo, 255 = nada está aceso) e evitar valores extremos de ciclo de trabalho. Aqueles. certifique-se de que após UEV os LEDs apaguem completamente por um ciclo PWM.
Evite definir r, g e b como 255 e lembre-se de invertê-los ao usá-los.
interrupções
A essência de uma interrupção é que, sob certas circunstâncias, o chip para de executar o programa principal e chama alguma função externa. As interrupções ocorrem devido a influências externas ou internas, incluindo o temporizador.
Quando criamos pela primeira vez um projeto no ST Visual Develop, além de main.c recebemos uma janela com um arquivo misterioso stm8_interrupt_vector.c, incluído automaticamente no projeto. Neste arquivo, uma função é atribuída a cada interrupção NonHandledInterrupt. Precisamos vincular nossa função à interrupção desejada.
A ficha técnica possui uma tabela de vetores de interrupção, onde encontramos os que precisamos:
13 Atualização/estouro do TIM2
14 Captura/comparação TIM2
Precisamos mudar o LED no UEV, então precisamos da interrupção #13.
Assim, em primeiro lugar, no processo stm8_interrupt_vector.c altere o nome padrão da função responsável pela interrupção nº 13 (IRQ13) para o seu:
{0x82, TIM2_Overflow}, /* irq13 */
Em segundo lugar, teremos que criar um arquivo main.h com o seguinte conteúdo:
@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;
}
Resta apenas ativar as interrupções. Isso é feito usando o comando assembler rim - você terá que procurá-lo em Manual de Programação:
//enable interrupts
_asm("rim");
Outro comando assembler é sim – desativa interrupções. Eles devem ser desligados enquanto novos valores estão sendo gravados na “memória de vídeo”, para que uma interrupção causada no momento errado não estrague o array.