
Continuação do artigo sobre como escrever seu próprio firmware para uma impressora 3D LCD de fotopolímero.
Nesta parte, continuarei a descrever as etapas do meu projeto:
2. Trabalho com uma unidade flash USB e arquivos nela
3. Controle de um motor de passo para mover a plataforma.
- Parte 1: 1. Interface do usuário.
- Parte 2: 2. Trabalhar com o sistema de arquivos em um stick USB. 3. Controle do motor de passo para o movimento da plataforma.
- Parte 3: 4. Exibindo imagens de camadas na tela de luz de fundo. 5. Cada pequena coisa, como controlar a iluminação e os ventiladores, carregar e salvar as configurações, etc. 6. Recursos adicionais para conforto e conveniência.
2. Trabalhar com uma unidade flash USB e arquivos nela
Nunca trabalhei com um host USB em microcontroladores antes. Como um dispositivo USB - Fiz firmware com a classe CDC (emulação de porta COM) e a classe HID, mas não funcionou com o host. Portanto, para agilizar o processo, criei toda a inicialização deste periférico no STM32CUBE. Como resultado, obtive um host funcionando no modo USB FS que oferece suporte a dispositivos de armazenamento em massa. No mesmo cubo, conectei imediatamente a biblioteca FatFS para trabalhar com o sistema de arquivos e arquivos. Então você só tinha que copiar os códigos-fonte recebidos em seu projeto e descobrir como trabalhar com eles. Acabou sendo fácil e não há muito o que descrever aqui. O arquivo usb_host.c de Cuba tem uma variável global Appli_state do tipo ApplicationTypeDef:
typedef enum {
APPLICATION_IDLE = 0,
APPLICATION_START,
APPLICATION_READY,
APPLICATION_DISCONNECT
}ApplicationTypeDef;
Em vários eventos do periférico host USB (em interrupções), esta variável pode assumir um dos estados listados, indicando o estado atual do host. No loop principal do programa, resta simplesmente rastrear as alterações nesta variável e reagir de acordo. Por exemplo, se seu valor mudou para APPLICATION_READY, então a unidade flash ou leitor de cartão está conectado e inicializado com sucesso, você pode ler os arquivos deles.
Também não há dificuldades com o FatFS - o Cube já o configura completamente e "conecta" ao host USB, então imediatamente após conectar o pen drive, você pode acessar as funções desta biblioteca para trabalhar com arquivos. É verdade que o cubo recém-atualizado inclui a biblioteca de versão antiga. Depois de atualizar seus arquivos para uma versão nova, tive que corrigir os nomes das definições da configuração do FatFS em alguns lugares do código-fonte cubano, porque eles mudaram na nova versão. Mas a atualização não trouxe nenhum problema particular, tudo correu de forma rápida e fácil.
Mas para o FatFS funcionar com o cirílico nos nomes de arquivos e diretórios, tive que mexer um pouco. Para que o FatFS leia corretamente os nomes cirílicos, é necessário habilitar o Unicode em sua configuração e, depois disso, todas as strings associadas ao FatFS devem estar apenas nesta codificação - nomes de disco, nomes de arquivo, etc. Ao mesmo tempo, o editor de texto no IDE e no FatFS suportam Unicode com diferentes posições do byte alto - um com Little Endian, o outro com Big Endian, portanto, é impossível simplesmente escrever fontes com textos Unicode. E eu não quero, para ser honesto. Foi quando tive que escrever conversores de ANSI e UTF-8 para Unicode e vice-versa, além de várias funções para trabalhar com strings de codificações diferentes em combinações diferentes. Por exemplo, copie uma string UTF-8 para uma string Unicode ou anexe uma string ANSI a uma string Unicode. No entanto, as strings ANSI parecem sernenhum lugar é deixado, todas as fontes são completamente convertidas para a codificação UTF-8.
Portanto, abrir um arquivo com um determinado nome agora se parece com isto:
tstrcpy(u_tfname, UsbPath); // (Unicode) (Unicode)
tstrcat_utf(u_tfname, SDIR_IMAGES); // (Unicode) (UTF-8)
tstrcat_utf(u_tfname, (char*)"\\"); // (Unicode) (UTF-8)
tstrcat(u_tfname, fname); // (Unicode) (Unicode)
Quando tudo funcionou rapidamente, eu queria verificar a velocidade de leitura de arquivos de uma unidade flash. Ler um arquivo de 10 MB em blocos de 4 KB mostrou uma velocidade de cerca de 9 Mbps, o que, em geral, era muito bom e adequado para mim.
Tentei estudar a questão de transferir este gabinete para DMA, mas descobri que os periféricos do host USB simplesmente não têm acesso ao DMA. Bem, ou não encontrei :) Portanto, parecia lógico organizar todos os buffers de leitura / gravação para arquivos USB em CCM (Core Coupled Memory) - uma área de 64 KB de RAM que também não tem saída DMA. Na mesma área de memória, faz sentido colocar outras variáveis / arrays que não funcionam com DMA, apenas para deixar mais memória na RAM regular. A propósito, pareceu-me que o próprio kernel funciona com essa memória um pouco mais rápido do que com a memória comum.
2.1 Interface do usuário do arquivo
A impressora Anycubic Photon S que possuo exibe uma lista de arquivos como ícones de visualização, 4 por tela. E, em princípio, é bastante conveniente - você pode ver o nome do arquivo, na imagem de visualização você pode ver aproximadamente que tipo de modelo. Portanto, segui o mesmo caminho - os arquivos são exibidos 4 peças por página na forma de imagens de visualização com o nome do arquivo.
A familiar pasta amarela é desenhada nos ícones de diretório e uma engrenagem nos arquivos de configurações. Apenas os arquivos para os quais a extensão se enquadra em um dos conhecidos são exibidos. Atualmente, estes são arquivos .pws (arquivos preparado pelo cortador para impressão) e arquivos .acfg (arquivos de texto com as configurações da impressora).
Como o firmware também funciona com diretórios que o usuário pode inserir, coloquei uma linha acima da lista de arquivos em que o caminho atual está escrito. Os botões para sair do diretório atual ou rolar para baixo e para cima aparecem apenas quando fazem sentido - ou seja, quando você pode sair do diretório atual ou rolar para baixo ou para cima na lista.

Um amigo meu, a quem mostrei tudo isso enquanto o firmware estava sendo escrito, sugeriu outra opção para a saída de arquivos - na forma de uma lista, uma tabela. Em primeiro lugar, mais arquivos cabem na página e, em segundo lugar, a lista é exibida muito mais rápido, já que não há necessidade de ler imagens de visualização de arquivos e desenhá-los com escala na exibição e, em terceiro lugar, em uma forma tabular, você também pode exibir além do nome e a hora em que o arquivo foi modificado pela última vez, o que às vezes é muito conveniente. É um pecado rejeitar uma boa ideia, por isso adicionei uma lista de mesas e, ao mesmo tempo, um botão para alternar entre as visualizações de "ícone" e "mesa". Os diretórios em forma de tabela são destacados com um fundo amarelo e a linha "DIR" é escrita em vez de hora-data:

A propósito, não há intriga sobre as imagens de visualização que são desenhadas para arquivos no modo de ícone. O firmware não analisa o arquivo inteiro para construir uma imagem a partir de um modelo 3D, como alguns pensam :) Esta imagem é salva no arquivo de impressão pelo próprio divisor, em um formato semelhante ao BMP - uma matriz de valores de cores de pixel de 16 bits. O tamanho da imagem de visualização é armazenado em campos especiais dentro do arquivo. Então, tudo é muito simples.
A única coisa que o firmware precisa forçar é dimensionar a imagem do arquivo para o tamanho do ícone na tela. O firmware executa a escala de uma forma muito simples: o fator de escala k é calculado(número fracionário) - a largura da imagem original é dividida pela largura da área de exibição na tela (o coeficiente de altura também é calculado e o maior dos dois valores é considerado) e, em seguida, pixels e linhas são retirados da imagem original para exibir na tela com um passo de k .
Dessa forma, você pode dimensionar em mais e menos. A qualidade do resultado escalado, é claro, deixa muito a desejar, uma vez que nenhuma interpolação é realizada, mas em uma tela tão pequena e de qualidade não muito alta ela é imperceptível, mas a velocidade de tal algoritmo é bastante alta.
Quando você clica no ícone ou linha do arquivo .pws, uma tela para visualizar as informações sobre o arquivo é aberta com a possibilidade de começar a imprimi-lo. Se o arquivo .acfg for clicado, o usuário será solicitado a carregar as configurações desse arquivo. Bem, se um diretório for pressionado, ele se tornará atual e a lista de arquivos será atualizada.
2.2 Vendo as informações do arquivo antes de imprimir
Como você observou corretamente nos comentários da parte anterior, Anycubic não possui nenhuma informação sobre o arquivo quando ele é selecionado. Os botões para iniciar a impressão e excluí-lo simplesmente aparecem. E isso é muito inconveniente - para descobrir o tempo estimado de impressão, ou o número de camadas, ou outros parâmetros deste arquivo, você precisa começar a imprimi-lo. Decidi não repetir esse defeito, e ao clicar no arquivo recortado, é aberta uma tela com as informações mais completas sobre ele:

nome do arquivo, tamanho, horário da última modificação e quase todos os parâmetros de impressão. Aqui, no entanto, o fato de a tela MKS DLP ter uma resolução de 480x320 afetou minhas mãos, enquanto os Enikubiks têm uma menor - 320x240, neste você não pode realmente mexer com um monte de texto.
2.2.1 Quanto ao cálculo do tempo de impressão, escreverei separadamente.
Este indicador não é armazenado no arquivo, ao contrário de todos os outros parâmetros. Sua impressora deve calcular de forma independente, com base nas informações que ele conhece. O mesmo Anycubic Photon S tem o hábito de ultrapassar este cálculo, e para baixo - por exemplo, ele promete 5 horas de impressão, enquanto na realidade ele imprime 6 horas. E Longer Orange 30 em geral, durante a impressão, muda desta vez para frente e para trás quase duas vezes. Decidi abordar este ponto o mais cuidadosamente possível. Em que consiste este tempo?- O tempo que leva para a plataforma descer a uma determinada velocidade até a altura da próxima camada.
- O tempo de pausa antes do início da exposição.
- Tempo de exposição da camada.
- O tempo que leva para a plataforma subir a uma determinada altura com uma determinada velocidade após a camada ser exposta.
Esses 4 parâmetros são somados, multiplicados pelo número de camadas e o tempo total de impressão é obtido. Se tudo é elementar com tempos de pausa e exposição - eles são mantidos com precisão de milissegundos, mas com o movimento da plataforma, tudo já é um pouco mais complicado.
A plataforma não pega a velocidade configurada instantaneamente, ela tem alguma aceleração, que é configurada nas configurações. Além disso, ao imprimir, esta é uma aceleração bastante pequena, uma vez que a plataforma deve começar a subir suavemente para que a última camada curada saia do filme sem dor no fundo do banho (sim, o polímero adere ao filme também, infelizmente).
Acontece que o movimento da plataforma consiste em três componentes - aceleração até que uma determinada velocidade seja atingida, movimento uniforme em uma determinada velocidade e desaceleração até a parada completa. E é aqui que as opções começam - por exemplo, a aceleração e altura de elevação especificadas não permitem que a plataforma atinja a velocidade especificada, ela ainda está acelerando no momento em que já precisa começar a desacelerar para parar na altura especificada. Ou a aceleração e a altitude são suficientes para que a plataforma acelere até a velocidade definida e percorra alguma parte do caminho em movimento constante antes de começar a desacelerar. Precisamos verificar tudo isso, calcular os tempos e as distâncias de cada componente.
Para ser honesto, minha cabeça estava girando quando escrevi a função de cálculo do tempo de impressão :) E como resultado, ainda recebi um pequeno erro. Por exemplo, o tempo real de impressão é 07:43:30 em vez das 07:34:32 estimadas.

Ou 05:48:43 em vez das 05:43:23 calculadas.

Mas, em princípio, esse erro me convinha. Tentei encontrar um erro nos cálculos, mas tudo parece estar correto aí. Muito provavelmente, a aceleração real ligeiramente não corresponde à especificada devido às peculiaridades do controle do motor de passo. Então, sem problemas, chegamos ao próximo estágio :)
3. Controle do motor de passo para o movimento da plataforma.
No início, pensei em escrever meu próprio controle de motor de passo. Não é nada difícil, tendo um driver normal na placa - defina a direção de rotação em um pino e direcione os pulsos de passos para o outro pino. Você precisa girar rapidamente - você aumenta a frequência de pulso, você precisa lentamente - você diminui.
Mas quando comecei a abordar esse problema mais especificamente, percebi que sua simplicidade engana. Não, você pode escrever o seu próprio, e funcionará, mas escrever de uma maneira que funcione bem é uma tarefa bastante grande. Os motores de passo não gostam muito de irregularidades nas etapas, por isso é necessário garantir uma boa uniformidade dos pulsos de etapa em uma faixa de frequência bastante ampla - de alguns hertz a dezenas de quilohertz. É necessário garantir um aumento e uma diminuição suaves na frequência dos pulsos de aceleração e desaceleração. É necessário contar com precisão os impulsos gerados para ter a garantia de saber em que posição a plataforma está agora. É necessário calcular o número de pulsos e o período de sua mudança de frequência por um período de tempo estritamente definido para fornecer a aceleração necessária.
Em suma, a tarefa, embora viável, é muito, muito volumosa, o que me levaria mais de um dia. Então, decidi tirar as funções de gerenciamento do motor do Marlin . Achei que seria fácil ...
Primeiro, peguei o arquivo stepper.cpp das fontes do Marlin - controlando diretamente o motor de passo. No entanto, descobri que seu trabalho depende muito do planejador de movimento do arquivo planner.cpp, então tive que pegá-lo também. Bem, para o heap, também peguei o arquivo endstops.cpp de lá - processando os interruptores de limite de eixo, uma vez que ainda precisava processar eventos deles, e aqui o planejador e o controle do motor já estavam associados a este arquivo para interruptores de limite.
Passei muito tempo tentando remover todos os arquivos desnecessários desses arquivos e desamarrá-los do resto do ecossistema Marlin. O fato é que o Marlin é afiado sob o controle de 6 ou 7 steppers ao mesmo tempo, enquanto seu trabalho pode depender da temperatura de diversos aquecedores, dos parâmetros do plástico, etc. O sistema é muito complicado aí. Tive que refazer muito, principalmente removendo eixos e extrusores desnecessários e me livrando de um monte de macros que eram úteis na versão original, mas muito perturbadoras na minha. Apenas para compreensão - o tamanho das fontes que tirei do Marlin foi reduzido de 346 para 121 KB. E cada linha teve que ser excluída com cuidado.
Naturalmente, no processo dessa poda difícil, me aprofundei um pouco mais no trabalho de todo o sistema, como ele funciona. Para mover o eixo, a posição alvo do eixo é transferida para o agendador por meio de uma de suas funções (a posição atual é armazenada pelo agendador). O planejador calcula o número de etapas e seus parâmetros para aceleração, movimento em linha reta e desaceleração e forma a partir desses dados um pacote de dados especial para a função de controle direto do motor (passo). Pode haver vários desses pacotes, o planejador calcula e cria um novo pacote seguinte para cada nova tarefa.
Stepper, trabalhando em uma interrupção do temporizador, em um estado livre, solicita o próximo pacote de dados do escalonador. Se o planejador tiver um pacote preparado, ele o distribui e o considera concluído. O Stepper leva o pacote recebido para o trabalho e começa a trabalhar as etapas do mecanismo de acordo com os dados dele. Até que seja concluído, o próximo pacote não é solicitado.
O que é curiosamente implementado no stepper é que em baixas velocidades ele emite um pulso de passo em cada interrupção, ajustando o temporizador para que a próxima interrupção ocorra após o período de tempo requerido. Quando a taxa de passos exigida excede um certo valor, o stepper começa a emitir vários passos em cada interrupção. Ao mesmo tempo, todos os tempos são escolhidos tão bem que a uniformidade dos passos é muito boa, por uma questão de curiosidade olhei para o osciloscópio.
O escalonador também sabe como "juntar" pacotes adjacentes. O que significa isso: se o agendador já tem um pacote preparado para o stepper e, em seguida, uma nova tarefa chega a ele, então ele forma o próximo pacote e altera o anterior de modo que, como resultado do processamento sequencial desses dois pacotes pelo stepper, um movimento suave seja obtido.
Deixe-me explicar com um exemplo. O planejador está livre, ele recebe a tarefa de mover o eixo para frente em 20 mm a uma velocidade de 30 mm / s. O planejador gera o primeiro pacote no qual descreve a aceleração de zero a 30 mm / s, movimento em linha reta nessa velocidade e desaceleração dessa velocidade para zero. Se, antes de o stepper pegar este pacote do agendador, o agendador recebe uma nova tarefa para mover este eixo mais 50 mm para frente, mas já a uma velocidade de 40 mm / s, então o agendador não irá apenas criar um novo pacote com aceleração de zero, mas alterar o primeiro pacote removendo a desaceleração e estendendo o movimento em linha reta por sua distância, e no segundo pacote criado, a aceleração não começará do zero, mas a partir da velocidade do pacote anterior.
O resultado é um movimento em que o eixo acelera para 30 mm / s, percorre 20 mm, depois acelera novamente para 40 mm / se percorre mais 50 mm, desacelerando até zero no final. Mas isso só acontece se o stepper ainda não conseguiu pegar o pacote anterior, caso contrário, essas duas tarefas serão processadas como dois movimentos separados com zero velocidade inicial e final em cada um deles. Portanto, a propósito, em impressoras com controle manual de plataforma, se você pressionar o elevador várias vezes seguidas em incrementos de 10 mm, a plataforma irá parar após o primeiro elevador de 10 mm e, em seguida, continuará a se mover sem parar até a altura total clicada pelo botão.
Na nova versão do Marlin, já apareceu um remédio contra esse movimento "espasmódico" - agora o agendador não dá um pacote stepper por um certo tempo após sua formação se esse pacote for o único pronto. Este tempo é reservado para espera - a próxima tarefa chegará para que você possa acoplá-la à existente.
3.1 Interface de controle de movimento da plataforma

Aqui, em geral, tudo é padrão e usual para impressoras de fotopolímeros. No topo está a seleção do passo do movimento do eixo, à direita estão os botões para mover o eixo para cima ou para baixo com o passo selecionado.
O botão "Home" é usado para zerar a plataforma (estacionamento, home), quando pressionado, a plataforma começa a se mover em direção ao interruptor de limite "home". Ao alcançá-la, a plataforma para, recua um pouco e, lentamente, (para maior precisão) atinge a chave fim de curso. Depois disso, o firmware definitivamente sabe a altura exata de elevação atual da plataforma.
Botão de configuração Z = 0 ”é usado para calibrar a altura da plataforma acima do monitor. Esse sistema de calibração é usado, por exemplo, em impressoras Anycubic, quando o ponto zero da plataforma (sua altura ideal acima da tela) está 1-2 mm abaixo do acionamento do interruptor de limite "inicial". E esse sistema de calibração me parece mais correto do que os sistemas que se popularizaram recentemente, quando a altura de acionamento do fim de curso é ao mesmo tempo a altura zero da plataforma.
Bem, o último botão é "Parar!" É uma parada incondicional e imediata do movimento da plataforma. A propósito, enquanto a plataforma estiver em movimento, você não pode sair desta tela, o botão "Voltar" não funcionará. Isso é feito apenas para que enquanto a plataforma está se movendo, o botão "Parar" esteja disponível instantaneamente.
3.2 Outros pontos no movimento da plataforma
Existem várias coisas que me incomodam terrivelmente em Anycubic Photon.
A primeira é por que o movimento manual da plataforma ocorre com a mesma aceleração de caracol que no modo de impressão? Ao digitar, essa pequena aceleração é útil, mas ao controlar manualmente o eixo, ela acelera por 2 segundos - é apenas um pesadelo. E a velocidade do movimento é razoável.
O segundo ponto - por que, quando a impressão é pausada, a plataforma sobe até a altura da pausa na velocidade especificada nos parâmetros de impressão? Inferno, esperar 15 segundos para a plataforma subir dois (apenas) centímetros é muito bom. Mas obrigado por se levantar. Em Orange 30, uma pausa não implica em levantar a plataforma nem por um milímetro, então nem mesmo fica claro por que ela está ali.
E o terceiro momento, que enfurece - após o fim da impressão, a plataforma sobe até o topo. Com a mesma velocidade especificada nos parâmetros de impressão - 1 mm / seg. Demora 100 segundos para subir de uma altura de 5 cm!
Portanto, em meu firmware, eu fiz velocidades e acelerações ajustáveis separadamente para o modo de impressão e separadamente para o controle manual da plataforma. Mas com duas limitações:
- Até que o eixo seja redefinido pelo botão Home, a velocidade de deslocamento será reduzida em três vezes. Isso ocorre porque, embora a impressora não saiba a altura exata da plataforma atual, há o perigo de esmagar o visor sem parar em alta velocidade (inércia, portanto) ou danificar o batente do eixo superior. Após zerar o eixo, a impressora já sabe exatamente a posição da plataforma e entram em vigor os limites de altura do software, que também são definidos nas configurações.
- Em altura inferior a 30 mm, a velocidade também é reduzida em três vezes, independentemente de o eixo estar zerado ou não. Isso evita que o fotopolímero salpique para fora do banho quando a plataforma for baixada muito rapidamente para dentro dele. Ou ao sair dele muito rapidamente.
Claro, existem outros parâmetros de eixo padrão nas configurações - o número de etapas por 1 mm, a direção do movimento, o trabalho dos interruptores de limite, etc. Se alguém estiver interessado, sob o spoiler, há um arquivo de configuração de texto com todos os parâmetros suportados. Esse arquivo com a extensão .acfg é comido pelo firmware diretamente da lista de arquivos, carregando os parâmetros, salvando-os na EPROM e aplicando-os imediatamente, sem reinicializar:
Conteúdo do arquivo de configuração
# Stepper motor Z axis settings
[ZMotor]
# .
# : 0 1. : 1.
# .
invert_dir = 1
# .
# : -1 1. : -1.
# -1,
# , . 1
# .
home_direction = -1
# Z . ,
# 0, - .
home_pos = 0.0
# .
# : -32000.0 32000.0.
# : -3.0
# .
# , .
min_pos = -3.0
# .
# : -32000.0 32000.0.
# : 180.0
# .
# , .
max_pos = 180.0
# .
# : 0 1. : 1.
# ,
# 1, - 0.
min_endstop_inverting = 1
# .
# : 0 1. : 1.
# ,
# 1, - 0.
max_endstop_inverting = 1
# 1 .
steps_per_mm = 1600
# ,
# , /. : 6.0.
homing_feedrate_fast = 6.0
# ,
# , /. : 1.0.
homing_feedrate_slow = 1.0
# , /2.
acceleration = 0.7
# , /.
feedrate = 5.0
# ( ,
# ..), /2.
travel_acceleration = 25.0
# ( ,
# ..), /. 30
# ,
# 5 /.
travel_feedrate = 25.0
# , .
current_vref = 800.0
# , .
current_hold_vref = 300.0
# ,
# . . 0
# .
hold_time = 30.0
# ,
# . .
# hold_time. 0 .
# , .
off_time = 10.0
# General settings
[General]
# (0.001 )
# .
# : 0 15000. : 700 (0.7 ).
buzzer_msg_duration = 700
# (0.001 )
# , .
# : 0 15000. : 70 (0.07 ).
buzzer_touch_duration = 70
# 180 .
# .
# : 0 1. : 0.
rotate_display = 0
# , .
# LCD-. -
# .
# : 0 15000. : 10. 0 .
screensaver_time = 10
E com isso vou terminar essa parte, e já tem muito texto :)
Como antes - ficarei feliz em responder perguntas e aceitar comentários.
- Parte 1: 1. Interface do usuário.
- Parte 2: 2. Trabalhar com o sistema de arquivos em um stick USB. 3. Controle do motor de passo para o movimento da plataforma.
- Parte 3: 4. Exibindo imagens de camadas na tela de luz de fundo. 5. Cada pequena coisa, como controlar a iluminação e os ventiladores, carregar e salvar as configurações, etc. 6. Recursos adicionais para conforto e conveniência.
Links
Kit MKS DLP no Aliexpress
Fontes de firmware originais do fabricante no GitHub
Esquemas do fabricante de duas versões da placa no GitHub
Minhas fontes no GitHub