Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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:

Pílula Azul STM32: 16 LEDs com driver DM634
STM8: Configurando seis pinos PWM
STM8: 8 LEDs RGB em três pinos, interrupções

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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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.

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
... 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:

Na ficha técnica podemos estar interessados ​​em:

  • 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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
dreno aberto, push-pull, push-pull alternativo, dreno aberto alternativo

"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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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):

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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):

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

Bem, vamos começar:

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

Abra a seção 25.3.3 com o título autoexplicativo “Configurando SPI no modo Master”:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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):

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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).

#define _SPI_CR1 0x00

#define BR_0        0x0008
#define BR_1        0x0010
#define BR_2        0x0020

_SPI2_ (_SPI_CR1) |= BR_0;// pclk/4

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):

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

#define DFF         0x0800

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

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:

#define MSTR        0x0004
#define SPE         0x0040

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

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”:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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”:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
<…>
Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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”:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

Nós escrevemos:

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

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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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.

Bem, algo como isto

#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

Nossos pinos estão localizados na porta B (endereço 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, consequentemente, você pode escrever definições para LAT, que serão alteradas pelos registros 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 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”:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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

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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

E eles, portanto, contêm bits que incluem o clock de SPI2, IOPB (porta de E/S B) e funções 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 pode ser encontrado aqui.

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.

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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.

O chip também tem Ficha de dados и manual de referência RM0016, no primeiro há pinagem e endereços de registro, no segundo - todo o resto. STM8 é programado em C em um IDE péssimo ST Desenvolvimento Visual.

Relógio e E/S

Por padrão, o STM8 opera na frequência de 2 MHz, isso deve ser corrigido imediatamente.

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Como vamos executar o PWM e conectar os LEDs, vamos dar uma olhada na pinagem:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

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

#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

Configuração PWM

Primeiro, vamos definir os termos:

  • 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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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):

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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).

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

#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 consiste em dois registradores de oito bits, tudo é simples:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

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:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

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:

#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 por fim, iniciamos o cronômetro no registro TIMx_CR1:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

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:

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

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
Habilitar saída principal

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Isso é tudo certo agora, o código ibid.

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

Multiplexador STM8

Multiplexação em STM8

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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
Interromper registro de habilitação

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

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.

Mudando a polaridade:

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

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:

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8
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:

#ifndef __MAIN_H
#define __MAIN_H

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

E finalmente, escreva esta função no seu 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;
}

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.

Todo o código - no GitHub.

Leitura de fichas técnicas 2: SPI no STM32; PWM, temporizadores e interrupções no STM8

Se pelo menos alguém achar este artigo útil, não o escrevi em vão. Terei prazer em receber comentários e observações, tentarei responder a tudo.

Fonte: habr.com

Adicionar um comentário